Rate Limiting

The Brandsearch API enforces rate limits and quotas to ensure fair usage. This page explains how they work, how to read your own limits, and how to handle them.

Rate limits

Rate limits protect the API from bursts. The same limits apply to every API key — they do not depend on your plan — and they are checked over several time windows at once, from one second up to one day. A separate set of limits applies per IP address, which matters if you run many keys from one server. It also counts requests with a missing, invalid or revoked key: a burst of rejected requests from one address can get 429 rate_limited before the key is even checked.

You do not need to know the numbers in advance, because every response tells you where you stand:

  • X-RateLimit-Limit is the ceiling of whichever window you are closest to filling;
  • X-RateLimit-Remaining is what is left in that window;
  • X-RateLimit-Reset is when it empties.

When you go over, the API answers 429 rate_limited with a Retry-After header, and names the window you filled in details.window (1s, 1m, 1h or 1d). See Handling rate limits below.

Rate limit exceeded response

{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded.",
    "details": {
      "retry_after": 12,
      "window": "1m"
    }
  }
}

Quotas

In addition to rate limits, each API key has a request quota: a ceiling on how many requests you can make per day and per month.

Your quota comes from your API subscription, which you manage from the API tab of your dashboard. A regular Brandsearch subscription includes a small API allowance, an API plan raises it, and without a paid subscription the allowance is zero. There is no fixed table on this page — plans change and allowances can be customised — so the API tells you your own numbers instead:

  • every response that returns data — searches, lists, single records — carries X-Quota-Daily-Limit and X-Quota-Monthly-Limit (your ceilings) next to X-Quota-Daily-Remaining and X-Quota-Monthly-Remaining (what is left);
  • GET /v1/usage returns the same ceilings and counters at any time, and it is unmetered, so checking costs nothing.

On current plans the daily ceiling is set equal to the monthly one, so the monthly figure is the one that matters in practice.

When a quota is used up, requests answer 429 quota_exceeded, with details.quota_type (daily or monthly) and details.limit, until that counter resets.

Quotas reset automatically — daily quotas reset at midnight UTC, and monthly quotas reset on the 1st of each month.


Free (unmetered) endpoints

Some endpoints don't count against your quota:

  • GET /v1/lookup — resolve a name/domain/handle to a brand ID
  • GET /v1/me — your account info
  • GET /v1/usage — live quota counters
  • GET /v1/facets/* — enumerate valid filter/sort/field values
  • GET /v1/swipe/mobile-feed/* — read the mobile feed and its folders, so watching for new posts costs nothing

These responses include X-Quota-Charged: false so you can verify the request was not metered.


Credits (per-row tracking)

In addition to request count, every metered response carries an X-Credits-Used header showing how many credits the request cost. Credits are charged per returned row, and a brand costs twice as much as an ad:

  • Brand rows cost 2 credits each. A page_size=20 brand list costs up to 40 credits. This applies wherever brand records are returned: GET /v1/brands, GET /v1/brands/discover, POST /v1/brands/query, POST /v1/brands/by-ids, and the two swipe-file endpoints that resolve a folder to brands.
  • Ad and post rows cost 1 credit each, so a page_size=20 ad list costs up to 20 credits.
  • Detail endpoints charge for the one record they return: 2 credits for a brand (GET /v1/brands/{brand_id}, GET /v1/brands/by-url/{url}), 1 credit for an ad or post (GET /v1/meta-ads/{ad_id}, etc.).
  • Emails cost 1 credit each, like ads: per email returned by a search, a discover sample, or a brand's email list, and 1 credit to fetch or download one email. Email aggregates cost a flat 1 credit.
  • Products cost 1 credit each: per product returned by a search, a market, a batch of URLs or a brand's catalogue, and 1 credit to open one product or download its image. An unknown product costs none.
  • Ad transcription (POST /v1/meta-ads/{ad_id}/transcription) charges 1 credit per transcript returned, and nothing if the call fails.
  • Free endpoints above charge 0 credits.

Credits are currently informational — there is no daily or monthly limit on credits yet, but counters (credits_used_today, credits_used_this_month) are exposed via GET /v1/usage.


Response headers

Every API response includes headers showing your current quota usage:

  • Name
    X-Quota-Daily-Remaining
    Type
    integer
    Description

    Requests remaining for the current day.

  • Name
    X-Quota-Monthly-Remaining
    Type
    integer
    Description

    Requests remaining for the current month.

  • Name
    X-Quota-Daily-Limit
    Type
    integer
    Description

    Your total daily limit.

  • Name
    X-Quota-Monthly-Limit
    Type
    integer
    Description

    Your total monthly limit.

  • Name
    X-Quota-Overrun
    Type
    string
    Description

    Present with value "true" when the response exceeded quota.

  • Name
    X-Quota-Overrun-Daily
    Type
    integer
    Description

    Number of daily overage requests (only when overrun).

  • Name
    X-Quota-Overrun-Monthly
    Type
    integer
    Description

    Number of monthly overage requests (only when overrun).

  • Name
    X-RateLimit-Limit
    Type
    integer
    Description

    Maximum requests allowed in the most-constrained rate-limit window.

  • Name
    X-RateLimit-Remaining
    Type
    integer
    Description

    Requests remaining in that window after the current one.

  • Name
    X-RateLimit-Reset
    Type
    integer
    Description

    Unix timestamp (seconds) when the most-constrained window expires and the counter resets.

  • Name
    Retry-After
    Type
    integer
    Description

    Seconds to wait before retrying (on 429 responses).

  • Name
    X-Credits-Used
    Type
    integer
    Description

    Credits charged for this request: 2 per brand returned, 1 per ad or post returned, 0 on free endpoints.

  • Name
    X-Quota-Charged
    Type
    string
    Description

    Set to "false" on unmetered endpoints (/v1/lookup, /v1/me, /v1/usage, /v1/facets/*).


Handling rate limits

When you hit a rate limit, the API returns a 429 status with a Retry-After header. The best practice is to implement exponential backoff:

Handling rate limits

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options)

    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '1')
      await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000))
      continue
    }

    return response
  }

  throw new Error('Max retries exceeded')
}

Was this page helpful?