Archive API docs
    Jump to

    Narrow by kind with a prefix — q: queries, m: mutations, t: types, g: guides, f: fields and arguments.

    Rate limits & plans

    The Archive API applies two independent limits to /api/v2: a request rate limit, which is the same on every plan, and a weighted cost limit, which your plan sizes. Compare the plans below, then price your own query with the calculator.

    The request rate limit

    Each workspace can send up to 5 requests per second to POST /api/v2, on every plan (the default; it is environment-tunable). This limit counts requests, not cost: a cheap query and an expensive one each count as one request. Requests over the limit are rejected with HTTP 429 and a Retry-After header:

    json
    {
      "errors": [
        {
          "message": "Rate limit exceeded for this workspace. Retry in 1 second(s).",
          "extensions": { "code": "RATE_LIMIT_EXCEEDED" }
        }
      ]
    }

    A rate-limit 429 also carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers.

    The weighted cost limit

    The second limit prices each request by how much work it asks for and draws that price from a token bucket: the bucket has a capacity, refills at a steady rate, and each request withdraws its cost. Each workspace has its own bucket. A workspace that belongs to an organization gets its capacity from the organization’s plan, but never shares a balance with the organization’s other workspaces. Two rules affect the cost of a request:

    • A per-request floor. Every request costs a minimum of 5, covering the fixed overhead of authenticating and loading workspace context. A refund never takes your net cost below this floor.
    • A post-execution refund. A connection’s cost is estimated up front from the page size you request, then reconciled after execution: if a page returns fewer records than you asked for, the unused per-record cost is refunded. Your actual cost is never more than the estimate.

    A request that asks for more than the bucket has left is rejected before any field is resolved, with HTTP 429 and a Retry-After header:

    json
    {
      "errors": [
        {
          "message": "Rate limit exceeded. Retry after 3 seconds.",
          "extensions": { "code": "RATE_LIMIT_EXCEEDED" }
        }
      ]
    }

    A rejected request costs nothing and doesn’t drain the bucket further. Both limits return RATE_LIMIT_EXCEEDED; the message identifies which one you hit, and Retry-After is authoritative in both cases.

    Rate limits by plan

    Each workspace draws from a token bucket: the bucket holds up to its capacity, refills at a steady rate, and every request withdraws its weighted cost.

    Weighted rate-limit bucket by plan. The request rate limit is 5 requests per second on every plan.
    Plan Bucket capacity Refill rate Full refill Bucket
    Trial 500 5 / s 100 s Own bucket — never pooled, even inside an organization
    Startup 5,000 50 / s 100 s Per workspace
    Growth 15,000 250 / s 60 s Per workspace
    Enterprise 60,000 1,000 / s 60 s Per workspace
    Agency 15,000 250 / s 60 s Each org workspace drains its own bucket at Growth numbers
    The request rate limit — 5 requests per second (300 per minute) — is the same on every plan and is not raised by any of them. Negotiated overrides replace these numbers per workspace or per organization, so read your live quota from the RateLimit-Policy header rather than hard-coding a capacity.

    Query cost calculator

    A connection’s page cost is search_base + ceil(per_edge × first), plus what you select inside it and any operation surcharge. Every request costs at least 5; every mutation costs a flat 50.

    38 weighted cost per request

    search_base
    30
    ceil(0.3 × 20)
    +6
    edges { node } wrappers
    +2
    What this buys on each plan
    Trial 7 req/min
    Startup 78 req/min
    Growth 300 req/min *
    Enterprise 300 req/min *
    Agency 300 req/min *

    * capped by the request rate limit, not by the bucket.

    The calculator opens on a real request — this one:

    query.graphql
    query ItemsDefault($first: Int, $after: String) {
      items(first: $first, after: $after) {
        totalCount
        edges {
          node {
            id
            provider
            type
            caption
            takenAt
            originalUrl
            archivePublicUrl
            currentEngagement {
              likes
              comments
              views
              earnedMediaValue
            }
          }
        }
        pageInfo {
          hasNextPage
          endCursor
        }
      }
    }
    variables.json
    {
      "first": 10
    }
    Headers
    Authorization: Bearer docs_demo_token_0000000000000000000000
    WORKSPACE-ID: 6ccefa76-e8ba-5ab0-9c60-e48a9e235a4a

    Reading the RateLimit headers

    Every /api/v2 response that carries rate-limit state includes the standard IETF RateLimit-Policy and RateLimit headers. These are the source of truth for your current quota. Read them rather than hard-coding any capacity from the table above:

    Rate-limit headers
    RateLimit-Policy: "<plan>";q=<capacity>;w=<window>
    RateLimit: "<plan>";r=<remaining>;t=<reset-epoch>

    In RateLimit-Policy, q is the bucket capacity and w is the number of seconds to refill the full bucket. In RateLimit, r is how much you have remaining and t is the epoch second at which the bucket is full again.

    Backing off

    • On a 429, honor the Retry-After header: wait the stated number of seconds before retrying.
    • Watch the RateLimit header’s r (remaining) and t (reset) values to pace your weighted-cost spend and avoid draining the bucket.
    • Prefer smaller pages and request only the fields you need; both reduce cost.