The Finstracker recommender, over HTTP.
One endpoint. Optional auth. Returns the same ranked card list the Finstracker app uses for "what should I swipe right now?" — so you can drop it into your own checkout, browser extension, or assistant.
GET /api/cards/best
Returns a ranked list of cards from the public catalog (and, when authenticated, from the user's own active merchant offers) for a given context — a merchant, a category, or a full monthly spend mix.
Query parameters
| Param | Type | Notes |
|---|---|---|
merchant | string | Case-insensitive substring match against the authenticated user's active Offers. Surfaces matching offers at the top of the list with reason: "active merchant offer". |
category | string | One of dining, groceries, travel, gas, streaming, transit, online_retail, drugstores. Used with amountCents for single-purchase shorthand. |
amountCents | integer | Optional purchase amount (cents). Drives the expectedPoints / expectedDollarValue math when category is supplied. |
spendMix | JSON | URL-encoded JSON object of {category: monthlyUSD}. Used in place of category+amountCents when you have a full picture of monthly spend. |
homeAirport | string (≤4 chars) | IATA code. Powers the redemption-fit factor — cards whose transfer partners actually fly from this airport float to the top. |
intent | earn | travel | Defaults to earn. When travel, the redemption-fit factor is applied even if goal isn't set. |
goal | string | Optional explicit goal: cashback | travel | hotels | sub | credit. |
lat, lng | number | Reserved for the Tier-6.3 PWA "best card nearby" use case. Accepted and echoed back in inputs; not currently used for scoring. |
excludeOwned | boolean | Defaults to true when authenticated. Drops cards already in the user's wallet from the catalog list. |
limit | integer | 1–20. Default 5. |
Authentication
Optional. Without a token, the endpoint returns catalog-based recommendations only. With a Authorization: Bearer <jwt> header from POST /api/auth/login, the endpoint additionally factors in:
- Your owned cards (excluded from recommendations unless
excludeOwned=false). - Your
travelProfile(home airport, hotel chains) merged with query-string overrides. - Your active
Offers(whenmerchantis supplied) — surfaced at the top of the list.
Rate limits
Best-effort 60 requests / minute / IP. There is no hard enforcement today; abusive callers will be IP-blocked at the edge. Please cache aggressively — recommendations are stable for a given input within a minute.
Example — anonymous, single-merchant context
curl -s 'https://finstracker.com/api/cards/best?merchant=Whole+Foods&category=groceries&amountCents=5000'
const url = new URL("https://finstracker.com/api/cards/best");
url.search = new URLSearchParams({
merchant: "Whole Foods",
category: "groceries",
amountCents: "5000",
});
const res = await fetch(url);
const { data } = await res.json();
console.log(data.recommendations[0]);
Example — authenticated, travel intent
curl -s 'https://finstracker.com/api/cards/best?intent=travel&homeAirport=SAT&spendMix=%7B%22dining%22%3A300%2C%22travel%22%3A200%7D' \
-H 'authorization: Bearer YOUR_JWT_HERE'
Response shape
All Finstracker responses use the standard {status, message, data} envelope.
{
"status": 200,
"message": "Recommendations computed",
"data": {
"recommendations": [
{
"cardId": null,
"cardKey": "chase-sapphire-reserve",
"displayName": "Sapphire Reserve",
"issuer": "Chase",
"pointType": "chase_ur",
"annualFee": 550,
"expectedPoints": 7200,
"expectedDollarValue": 540.00,
"score": 675.00,
"redemptionFit": 1.25,
"reason": "earns CHASE UR → transfers to United, which flies from SAT — 3× travel × $200/mo × 1.5¢ = $108/yr"
}
],
"inputs": {
"spendMix": {"travel": 200, "dining": 300},
"goal": "travel",
"homeAirport": "SAT",
"intent": "travel",
"lat": null,
"lng": null,
"excludeOwned": false,
"authenticated": true,
"excludedKeys": []
}
}
}
Per-recommendation fields: cardId is non-null only for active-offer hits backed by a user's own card; expectedPoints is the gross yearly points / cash-equivalent units; expectedDollarValue is the dollar value after annual-fee deduction; redemptionFit is the 0.5–1.25 factor from redemption-fit scoring.
Build something with it.
The endpoint is free and unauthenticated. The deeper recommendations come once you sign in.
Create a free account