API · Reference

Checking Coverage Before You Call

CoinMarketCap APIUpdated 14 September 2026 · 9 min read
Checking coverage before you call, shown as the word APIs above a grid of stacked data pads with percentage change readouts.

A covered cryptocurrency with nothing generated for one question looks, to a client that does not check, exactly like a cryptocurrency CoinMarketCap does not cover at all. Both come back without content, and neither comes back as an error.

Those are different problems with different fixes, and /v5/cmc-ai/coins/map exists to tell them apart in one call before you spend anything on the second.

Key Takeaways

  • Neither empty state is an error. On the coverage map an uncovered coin is absent from data.coins[]; on either endpoint a covered coin with nothing generated returns an empty insights[]. Both arrive inside an HTTP 200.
  • available_question_keys is the field that matters. It lists what a coin has content for right now, so you can decide what to render before asking for it.
  • Six of the eight questions sit at 94 or above out of 100. The two price questions sit at 29 and 21, by design, because they exclude each other and only fire on a real move.
  • Empty results are free. A covered coin returning no insights is not charged, and neither is a coin skipped by skip_invalid.
  • Coverage rotates. It tracks the top 100 by market cap, so it is a set to read, never a list to hardcode.

Two Empty States, Two Fixes

Not covered Covered, nothing generated
What it means The asset is outside the current coverage set The asset is in the set, but this question has no content at this moment
On /coins/map Absent from data.coins[] Present, with an empty or partial available_question_keys
On /coins/latest Not documented, see below Present, with an empty insights[]
HTTP status 200 200
error_code No error No error
Charged? Not charged on the map Free
The fix Hide the surface entirely, or show a different data source Keep the surface, show an honest empty state, check again later

The distinction matters because the two call for opposite product decisions. An uncovered asset should not display a CMC AI section at all. A covered asset with nothing generated should keep its section and say so, because content is likely to arrive on the next regeneration.

On the map endpoint the uncovered case is documented explicitly: a filter that matches a coin outside the coverage set is not an error, that coin is simply absent from data.coins[], and total_size reflects the matched count rather than what you asked for.

Why the map call is worth making

The insights endpoint does not document the same case. It documents what happens for a covered coin with no content, and what happens for an identifier it cannot resolve at all, but not for a real cryptocurrency that simply sits outside the top 100. The map tells you which coins are in the set, so you never have to find out what the insights endpoint does with one that is not.

What the Map Returns

GET /v5/cmc-ai/coins/map returns the cryptocurrencies CMC AI currently writes Coin Detail Page content for. No answer bodies come back, which is what makes it cheap enough to call first.

Field Type What it tells you
crypto_id integer CoinMarketCap ID, an integer rather than a string
name, symbol, slug string The asset, three ways
url string Its Coin Detail Page
available_question_keys array of strings Which fixed questions have content now. May be empty
num_insights integer Total insights available, across all types
last_generated_at string or null ISO 8601 UTC of the most recent generation. Null if nothing has ever been generated

Every field and type from the CMC_AI_Coverage_Coin_object schema.

available_question_keys draws from the same eight coin-level keys as the insights endpoint: price_up, price_down, future_price, sentiment, latest_news, overview, roadmap and codebase. For what each one asks and returns, see the question keys reference.

All filters are optional, and a call with no parameters returns the full supported set. sort takes cmc_rank, crypto_id or name, defaulting to cmc_rank. Pagination uses start and limit, which default to 100 and cap at 250, and the response carries total_size and has_more after the array.

The whole call costs 1 credit regardless of how many coins come back, and repeat calls returning the same content are not charged at all.

How Complete Coverage Is, Honestly

Coverage is the current top 100 cryptocurrencies by market cap. CoinMarketCap publishes a per-question figure for how many of those 100 have content, under the heading “Coverage of the 100” on the CMC AI launch announcement. Those figures are the left column of the comparison below.

Six of the eight sit at 94 or above. The two price questions sit far below, at 29 and 21, and the reason is structural rather than a gap. They fire only on a move worth explaining, and a coin never gets both in the same generation, so at any moment roughly half the set has neither.

The Rendered Pages Tell a Different Story

CMC AI answers are also published as pages under /cmc-ai/, and it is tempting to judge coverage by looking at them. That produces a very different picture. Measured 11 September 2026 across the live top 100 by market cap, by fetching every coin’s /cmc-ai/ pages and recording which questions render:

question_key Published, from the API Measured on the rendered pages
future_price 99 99
latest_news 99 99
roadmap 99 99
sentiment 98 99
overview 96 99
codebase 94 99
price_up 29 83
price_down 21 90

Left column from CoinMarketCap’s published figures. Right column measured 11 September 2026 across all 100 top-ranked assets, 400 page fetches, reproducible.

The six non-price questions agree closely, which is a useful independent check on the published figures. The two price questions do not agree at all, and the gap is close to a factor of three.

The reason is that the two surfaces measure different things. A price-analysis page accumulates history: it keeps previous days’ answers, each stamped with its generation date. The API reports current state. Of the 100 assets, 80 render both price_up and price_down on the same page, which the API cannot return for a single coin at a single moment because the two are mutually exclusive.

The generation dates confirm it directly

Checked across 10 assets on 11 September 2026: 40 dated answers, 20 up and 20 down, and not one asset carried both on the same date. So the page and the API do not disagree about the rule. They disagree about the window: the page shows every generation, the API shows the current one.

So a developer who eyeballs the published pages and concludes that coverage is near-total will be wrong about what the API returns, by a wide margin, on exactly the two questions most likely to sit at the top of a product surface. Judge coverage from available_question_keys, not from the pages.

Two smaller findings from the same measurement, both worth designing around.

Stablecoins have no price-analysis page at all. Tether, USDC, Dai, Ethena USDe, USD1 and TrueUSD each return HTTP 404 for /cmc-ai/<slug>/price-analysis/ while their other pages return 200. That is sensible for a pegged asset, and it means a product that shows “why did this move today?” across a portfolio should expect nothing for the stablecoin holdings rather than treat it as a failure.

One top-100 asset has no /cmc-ai/ pages at all. Jupiter, ranked 69 at the time of measurement, returns 404 on all four page types. That may be a slug mismatch between the listings API and the /cmc-ai/ URL space rather than a coverage gap, and it is worth checking, because a slug that resolves in one place and not the other is the kind of thing that breaks a link builder silently.

Taken together, roughly half of the 100 have a price explainer at any given moment. So a product that renders “why did this move today?” as a permanent fixture will show an empty state about half the time. A product that renders it conditionally will show it when it has something to say. That is the single most useful thing to know before designing the surface, and it is why the coverage check is worth a call.

codebase at 94 is the lowest of the non-price questions, which is worth knowing if you are building a developer-facing or fundamentals view where that question carries weight.

Treat all eight as a snapshot rather than a guarantee. Coverage rotates with the ranking, and each question regenerates on its own clock, so the honest way to use these figures is to size your expectations, not to hardcode them.

Check First, Then Fetch

The pattern is two calls: read the map, then request only what exists.

python
import os
import requests

BASE = "https://pro-api.coinmarketcap.com/v5/cmc-ai"
HEADERS = {
    "Accept": "application/json",
    "X-CMC_PRO_API_KEY": os.getenv("CMC_API_KEY"),
}


def coverage():
    """Read the coverage set once. Costs 1 credit regardless of size."""
    r = requests.get(f"{BASE}/coins/map", headers=HEADERS, params={"limit": "250"})
    r.raise_for_status()
    data = r.json()["data"]
    return {c["crypto_id"]: c for c in data.get("coins", [])}


def plan_render(crypto_id, wanted, cover):
    """Decide what to render before spending a credit on it.

    Returns one of three states so the caller can branch on intent
    rather than on an empty response it has already paid for.
    """
    entry = cover.get(crypto_id)
    if entry is None:
        return {"state": "not_covered", "keys": []}

    have = set(entry.get("available_question_keys") or [])
    ready = [k for k in wanted if k in have]

    if not ready:
        return {"state": "covered_pending", "keys": [],
                "last_generated_at": entry.get("last_generated_at")}

    return {"state": "ready", "keys": ready,
            "last_generated_at": entry.get("last_generated_at")}


def fetch(crypto_id, keys):
    """Only called when the map says there is something to fetch."""
    r = requests.get(
        f"{BASE}/coins/latest",
        headers=HEADERS,
        params={"crypto_id": str(crypto_id),
                "question_key": ",".join(keys),
                "sources_limit": "10"},
    )
    r.raise_for_status()
    coins = r.json()["data"].get("coins", [])
    return coins[0].get("insights", []) if coins else []

Parameter names, defaults and the available_question_keys field verified against the shipped reference, 10 September 2026.

Three things that pattern buys you.

  • A decision before a charge

    /coins/latest costs 1 credit per cryptocurrency returned. Checking the map first costs 1 credit total, however many coins you check, so on any batch above one asset the check pays for itself.

  • A distinguishable empty state

    plan_render returns not_covered and covered_pending as different states, which is what lets the UI behave differently rather than showing the same blank box for both.

  • A staleness signal for free

    last_generated_at comes back on the map, so you know how old a coin’s content is before deciding whether to re-fetch it.

Designing the Empty State

Because both empty states arrive inside a successful response, the failure mode here is not an exception. It is a heading with nothing under it. Three rules follow from the field behaviour rather than from taste.

  1. Never render a question heading before its answer has arrived.

    Drive the headings off available_question_keys, not off the full list of eight. A section that exists only when it has content cannot render empty.

  2. Say which of the two empty states you are in.

    “No AI coverage for this asset” and “No recent update for this question” tell a user different things, and both are more useful than a spinner that never resolves.

  3. Do not treat an empty result as a retry.

    A covered coin with nothing generated will have nothing generated until its next regeneration, which is between 1 and 24 hours away depending on the question. Polling faster returns the same empty response, and last_generated_at tells you where you are in that cycle.

For batch requests, skip_invalid=true is the companion setting. By default a single unresolvable identifier fails the whole request with error code 4001. With it set, unresolvable identifiers are skipped and the valid ones still return, and the skipped coins are not charged. For any batch assembled from user input, that is the safer default.

For what each question is called and what it returns, see the question keys reference. For 4001, 1006 and the rest of the error surface, see the error code reference.

Plan Access

The coverage endpoint carries the same Phase 1 gating as the rest of the CMC AI family: Enterprise only in Phase 1, billed against the general plan allowance rather than a separate one, and not reachable without a key. A second phase is planned that adds a dedicated monthly AI credit allowance and extends access beyond Enterprise.

That means the coverage check is not a way to evaluate CMC AI from a lower tier. A call from an unauthorised plan returns HTTP 403 with error code 1006, which no retry resolves.

FAQ

How do I know whether you have data for a coin before I ask for it?

Call GET /v5/cmc-ai/coins/map. It returns every cryptocurrency currently covered, and per coin it reports available_question_keys for what has content now, num_insights for how many, and last_generated_at for how recent. It costs 1 credit regardless of how many coins come back.

Which cryptocurrencies are covered?

The current top 100 by market cap. The set rotates as the ranking changes, so read it rather than hardcoding it.

Is an empty response an error?

No, and there are two distinct versions of it. On the coverage map, a coin outside the coverage set is absent from data.coins[]. On either endpoint, a covered coin with nothing generated is returned with an empty insights[]. Both arrive with HTTP 200 and no error code, and the second is not charged.

Why do the price questions have such low coverage?

Because the two price keys never appear together on one coin in one generation, and neither fires unless the price actually moved enough to explain. At 29 and 21 of 100 they add up to roughly half the set having a price explainer at any moment, which is expected behaviour rather than a coverage gap.

Does checking coverage first actually save credits?

On more than one asset, yes. The map costs 1 credit for any number of coins; /coins/latest costs 1 credit per cryptocurrency returned. Checking 50 coins costs 1 credit and tells you which of them are worth the 50.

How do I stop one bad symbol failing my whole batch?

Pass skip_invalid=true. Without it, any unresolvable identifier fails the request with error code 4001. With it, the unresolvable ones are skipped, the valid ones return, and the skipped coins are not charged.

How stale might the content be?

last_generated_at tells you exactly, per coin. Regeneration runs about hourly for the price questions, about every 8 hours for future_price, sentiment and latest_news, and about every 24 hours for overview, roadmap and codebase.

What does last_generated_at: null mean?

Nothing has ever been generated for that coin. It is covered, so it may get content later, but it has none yet and has never had any.

Where to Go Next

For every question key with its cadence and coverage, read the question keys reference. For what each endpoint charges, read the per-endpoint credit reference.