Rate Limiting

The Brandsearch API enforces rate limits and quotas to ensure fair usage. This page explains the limits by tier and how to handle them.

Rate limits by tier

Rate limits are applied per API key. Each tier has different limits across multiple time windows.

  • Name
    Starter
    Type
    tier
    Description

    3 req/s, 15 req/min, 300 req/hr, 3,000 req/day

  • Name
    Outscaler
    Type
    tier
    Description

    10 req/s, 60 req/min, 1,000 req/hr, 10,000 req/day

  • Name
    Agency
    Type
    tier
    Description

    30 req/s, 200 req/min, 5,000 req/hr, 50,000 req/day

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 daily and monthly request quotas.

TierDaily QuotaMonthly Quota
Starter3,00050,000
Outscaler10,000200,000
Agency50,0001,000,000

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

You can fetch live counters at any time with GET /v1/usage, which is unmetered.


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

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 rows were charged:

  • List endpoints charge 1 credit per returned row (so a page_size=20 response costs up to 20 credits).
  • Detail endpoints (GET /v1/meta-ads/{ad_id}, etc.) charge 1 credit.
  • 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 (rows on list, 1 on detail, 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?