Authentication & errors
API keys, rate limits, the response envelope and error handling.
API keys
Keys look like lfx_live_ followed by 32 characters. Send yours in the x-api-key header (or Authorization: Bearer <key>).
curl https://api.landedfx.com/api/v1/usage -H "x-api-key: $LANDEDFX_KEY"Endpoints that do work for you need a key: quotes, swap quotes, rate locks and carry quotes. Market data is public without one, at a lower rate limit. A key that is sent but not valid is always rejected, even on public endpoints, so a typo never fails silently.
We store only a hash of your key, so it is shown once, when it is issued. If you lose it, ask for a new one and we revoke the old one.
Plans and limits
| Plan | Price for 30 days | Requests per minute | Calls per month |
|---|---|---|---|
| Free | $0 | 60 | 5,000 per key |
| Growth | $99 | 300 | 250,000 across your account |
| Scale | $499 | 1,200 | 2,000,000 across your account |
| Without a key | 30 per IP | Public endpoints only |
Need more than Scale? Email hello@landedfx.com.
Upgrade under Plan and billing in the dashboard. You pay in USDC on Base from your wallet, and the plan applies to all your keys within a minute. Payments don't renew by themselves: we email you 7 days and 1 day before a plan ends, and your keys keep their limits for 3 more days before moving to Free. Moving up credits the unused part of your current plan. See Fees.
Every call made with your key counts toward the month, except calls rejected for rate limiting. Months run in UTC and reset on the 1st.
Responses carry your limits in headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit, X-RateLimit-Remaining | This minute's limit and what's left |
X-Quota-Limit, X-Quota-Remaining | This month's calls and what's left (plans with a cap) |
Usage
GET /usage returns your plan, calls this month, what's left, when it resets, and calls broken down by endpoint and by day.
{
"code": 200,
"message": "Usage retrieved successfully",
"data": {
"key": { "keyId": "key_a3f08cd4ce47", "name": "Acme Remit, production" },
"month": "2026-11",
"plan": { "id": "sandbox", "label": "Free", "ratePerMinute": 60, "monthlyCalls": 5000 },
"calls": 2,
"remaining": 4998,
"resetsAt": "2026-12-01T00:00:00.000Z",
"byRoute": [{ "route": "POST /forwards", "calls": 1, "errors": 0 }],
"byDay": [{ "day": "2026-11-07", "calls": 2, "errors": 0 }]
}
}The envelope
Every response, success or error, has the same shape:
{
"code": 201,
"message": "Quote generated successfully",
"data": { "…": "…" }
}Validation errors add an errors array:
{
"code": 400,
"message": "Validation failed",
"errors": ["amount must be a positive decimal with up to 18 decimals"]
}Amounts
Amounts are decimal strings ("150000", "725.50"), never JSON numbers, so nothing is lost to floating point.
Errors
| Code | When | What to do |
|---|---|---|
400 | Unknown asset or rail, bad amount | Check notation and GET /currencies/{code} for rails |
401 | Missing, invalid or revoked key | Send a valid x-api-key |
404 | Unknown quote id or route | Check the id |
422 | No usable route at this size right now | Read the message: it names how many ads were excluded and why. Try a different size, rail or time |
429 | Over the per-minute limit, or this month's calls are used up | Per minute: back off and retry. Monthly: wait for the 1st or upgrade |
502 | An upstream venue or solver failed | Retry; the quote's warnings name the venue |