## Best practices and FAQ

How to build a client against the CoinWorldCap API that stays correct and inside its rate limit.

### Identify by `id`, not by `symbol`

A symbol is not unique — one ticker can return several unrelated assets
([`/assets`](#get-openapiv1assets)). Look an asset up once, by `slug` or by contract address,
keep the `id` that comes back, and send that from then on. On `/exchanges` a `slug` is not
unique either, so keep the exchange's `id` too.

Assets and RWAs share one id column, but an RWA's `id` finds nothing on `/assets` and an
asset's finds nothing on `/rwa`. Exchanges and people are numbered separately, so the same
number can name an asset and an unrelated exchange. Keep track of which endpoint an `id` came
from. A DEX
token has no numeric id to send: it is named by `chain_slug` and `address`
([DEX tokens: what the endpoints share](#dex-tokens-what-the-endpoints-share)).

### Poll no faster than the data changes

Each dataset is rebuilt on its own schedule, and calling more often than that spends your
allowance on the same numbers. Cache a response for about its dataset's interval;
[Data methodology](#data-methodology) lists the intervals we publish and the field that dates
each response.

To follow many assets, ask for them together: `/assets`, `/quotes` and `/rwa` take up to 100
ids in one `id` parameter. Send `limit=100` with them — the default page is 20 rows — and a
watchlist of 100 costs one request rather than one per asset.

### Retry only what is retryable

| Response | What to do |
|---|---|
| `429`, code `42900` | Wait at least `Retry-After` seconds, then retry. [Rate limiting](#rate-limiting) explains why the wait can exceed a minute. |
| `503`, code `50300` | Retry with exponential backoff and random jitter — say 1, 2, 4 and 8 seconds, then give up for a while. It carries no `Retry-After`. |
| `500`, code `50000` | Retry a few times with the same backoff. If it persists, tell us, quoting the `X-Request-Id`. |
| `400`, `401`, `404` | Do not retry unchanged: the same request gets the same answer. Fix the parameters, the key or the `id`. The exception is `/global`'s `404` — see [Errors](#errors). |

### Narrow a list instead of paging through it

`offset` stops at 100,000 ([Errors](#errors)). Filter or sort so that the rows you want come
first. `/dex/tokens/trades` and `/dex/tokens/liquidity` page
by cursor instead: pass `next_cursor` back exactly as it came until it is `null`.

### Fetch history in ranges

With `time_start`, a range that holds more points than one response may carry is a `400`, not a
shorter answer ([History: what the endpoints share](#history-what-the-endpoints-share)). To load a
long history, ask for consecutive ranges that each stay within that cap. Both ends of a range are
included, so start the next range one `interval` after the last point you received. On
`/earn/history` [the cap counts every product](#get-openapiv1earnhistory): ask for one platform at
a time with `provider_id`. For a recent chart, leave out `time_start`.

To keep a stored history current, ask again from a little before your newest point and replace
points by `time`: recent points can still be revised for a while after they first appear.

### Check parameter names

A query parameter an endpoint does not read is ignored, not rejected, so a misspelt filter —
`chain_slg=solana` — returns the unfiltered list with a `200`. Compare the names you send with
the endpoint's parameter table.

### FAQ

**How often is the data rebuilt, and how old is a response?** [Data methodology](#data-methodology)
lists the intervals we publish, and names the field that dates each response.

**Why does an `id` return nothing?** Usually one of three things. The `id` belongs to another
family: an RWA's `id` sent to `/assets` matches nothing, because RWAs are listed by `/rwa`. Or a
filter on a list matched nothing: that is an empty page, not a `404`. Or the
resource does not exist: an endpoint that returns one resource answers `404` with code `40400`,
and retrying will not help ([Errors](#errors)).

**Where does each history start?** It depends on the dataset, and on the asset:
[Data methodology](#history) lists them. A daily range that starts before the history does is
not an error: it returns fewer points, or none. Hourly ranges have a limit of their own
([History: what the endpoints share](#history-what-the-endpoints-share)).

**Why is an exchange missing from `/exchanges`?** It lists centralized exchanges only; a
decentralized exchange's `id` or `slug` matches nothing there
([`/exchanges`](#get-openapiv1exchanges)).
