Skip to main content
Arcmira bills premium usage by rows, not by API calls. Rows are the unit of indexed content surfaced to your account: mention and appearance rows, related-entity rows, commercial rows, transcript blocks. API and web usage share the same row pool. A row counted for a web request is not counted again via the API. There is no separate API tier; heavy automation uses rows faster than browsing.

What counts as a row

  • Mention / appearance rows returned in a list: one row each. The first 5 rows of each distinct pull are free, and repeating the exact same request within 7 days is not re-billed.
  • Related-entity rows on aggregated views: one row each, same 5-row allowance.
  • Commercial rows (/v1/recommendations, /v1/channels/{id}/sponsors, details=full enrichment items): 10 rows each, because the commercial pipeline is more expensive to run and curate. No free allowance.
  • Transcripts: 75 rows per 15-minute block of video, minimum one block, unlock permanent per account. See Transcripts.
  • Free: /v1/me, /v1/health, /v1/openapi.json, search, lookup, discovery search (including its recommendations_summary), recommendations_summary aggregate blocks, teasers, and all Community Review submissions and corrections.
Practical consequences: a 40-sponsor list costs 400 rows, ten times a 40-mention page; details=full charges the mention row plus 10 rows per attached commercial item, so omit it when you don’t need the overlay; searches are free but the rows you then fetch are not.

Checking usage

curl https://api.arcmira.com/v1/me \
  -H "Authorization: Bearer $ARCMIRA_API_KEY"
{
  "user_id": "usr_...",
  "tier": "pro_plus",
  "scopes": ["read", "recommendations:read"],
  "rate_limit": 240,
  "recommendations_api_enabled": true,
  "usage": {
    "rows_used": 1234,
    "rows_remaining": 98766,
    "monthly_rows": 100000,
    "current_spend_cents": 0
  }
}
current_spend_cents reflects on-demand spend in the current period; it stays 0 until you exceed the allotment with on-demand enabled. /v1/me is free: check it before large pulls.

Exceeding your monthly allotment

ConditionOutcome
Allotment exhausted, on-demand enabledRequests keep serving; each row over the allotment bills on demand, up to your spend cap
Allotment exhausted, on-demand disabled402 quota_exceeded until the next billing cycle
Toggle on-demand usage and set a hard spend cap in Settings → Billing.

Rate limits

Separate from rows: every key is throttled per minute over a fixed 60-second window (the counter resets on the minute boundary).
Plan tierRequests per minute
Free60
Paid (Pro, Pro+, Ultra)240
Teams / Enterprise600
Each key has its own bucket; mint separate keys per agent, environment, or job from Dashboard → API Keys. Per-key overrides are common for high-throughput integrations: ask via support. Every authenticated response (including errors past the key check) carries the bucket state:
HeaderDescription
RateLimit-LimitRequests allowed per minute for this key
RateLimit-RemainingRequests left in the current window
RateLimit-ResetUnix epoch (seconds) when the window resets
Retry-AfterOn 429 only: seconds to wait. Always honor it
Throttle errors are 429 rate_limit_exceeded; quota exhaustion is 402 quota_exceeded. They are different problems with different fixes.

Backoff pattern

async function callWithBackoff(url: string, init: RequestInit, maxRetries = 5) {
  for (let attempt = 0; ; attempt++) {
    const res = await fetch(url, init);
    if (res.status !== 429 || attempt >= maxRetries) return res;
    const retry = Number(res.headers.get('Retry-After') ?? '1');
    await new Promise((r) => setTimeout(r, retry * 1000));
  }
}
import asyncio, httpx

async def call_with_backoff(client, method, url, max_retries=5, **kw):
    for attempt in range(max_retries + 1):
        r = await client.request(method, url, **kw)
        if r.status_code != 429 or attempt == max_retries:
            return r
        await asyncio.sleep(int(r.headers.get('retry-after', '1')))

Per-key tracking

Each key tracks lifetime total_requests, total_rows_used, and total_cost_cents, visible in Dashboard → API Keys. Separate keys per integration make usage, rate limits, and Community Review reputation easy to attribute.

Reducing costs

  • Cache resolved ent_* IDs; don’t re-run lookups.
  • Pass entity_id instead of entity_name so no resolution rides inside metered requests.
  • Scope pulls with date_from / date_to.
  • Pull larger pages (up to limit=100) when iterating: rows cost the same, the request count drops.
  • Quote transcripts before unlocking (meta.quote is free) and check usage.rows_remaining first.