vlayervlayer docs
Using the API

Rate limits

The per-minute limits on each group of Social Data API routes, how they are counted, and what to do on a 429.

Data lookups

Each client IP may make up to 60 requests a minute to each of these route groups:

Route groupLimit
GET /v1/comments60 requests per minute
GET /v1/stats and POST /v1/stats/batch, together60 requests per minute
GET /v1/profile60 requests per minute
POST /v1/stats/batch, URLs60 URLs per minute, every URL in the body counting as one

A batch counts twice: once as a request in the stats group, and once per URL against the batch URL limit. Batching saves round trips, but the URLs in a batch are not free of the limit. A batch that would go over is rejected whole and nothing is read, and its URLs still count until the window ends, so a smaller batch straight after is rejected too.

Limits are counted in fixed one-minute windows. They are per client IP, not per token: they are keyed on the address the request arrives from, so every caller behind a shared egress address, such as an office network or a NAT gateway, shares them, and tokens used from the same IP share them too. The per-group limits are checked before the token, so a request with a missing or wrong token counts against them as well. The batch URL limit is checked after the token, and every URL in the body counts before the body is validated: a batch of 26 URLs uses 26 of the minute's URLs and is then rejected with 422. Plan for the numbers in the table: at times more requests a minute may get through, but that is not something to build on.

Other limitValueWhen you exceed it
URLs in one batch1 to 25422 VALIDATION_ERROR
Batch duration100 secondsUnfinished URLs return BATCH_TIMEOUT

Need more?

Higher limits are available on request. Book a call (opens in a new tab) with the volume you expect, in requests per minute at peak. See High-volume integration for how to ramp up.

When you hit a limit

A request over a limit is answered with 429 RATE_LIMITED and a Retry-After header, in seconds, saying when the current window ends. Wait that long, then retry; retrying sooner is answered with another 429. A rejected request is not billed. The API sends no X-RateLimit-* headers, so the first sign of the limit is the 429 itself.

async function getWithRetry(url: string, attempts = 3): Promise<Response> {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch(url, { headers: { Authorization: `Bearer ${API_TOKEN}` } });
    if (res.status !== 429) return res;
    const wait = Number(res.headers.get("Retry-After") ?? 60);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
  throw new Error("Still rate limited");
}

429 SCRAPING_RATE_LIMITED is different: reads of the platform are temporarily rate limited, whatever your own usage. It also carries Retry-After; see Errors.

To stay under the limits, spread requests evenly across the minute instead of sending them in bursts, and don't request the same post again sooner than its counts can change.

Engagement verification

Engagement verification routes have limits of their own:

LimitValueWhen you exceed it
Post metrics refresh, one challengeOnce every 5 minutes429 RATE_LIMITED
Post metrics refresh, any challenge200 per minute per client IP429 RATE_LIMITED
Post check (/admin/posts/thumbnail)10 per post per 5 minutes, and 100 per minute per token429 RATE_LIMITED
Likers refresh10 per minute per client IP, across every challenge429 RATE_LIMITED
Likers refresh maxCount1 to 5000, default 1000—
limit on a paginated list route100400
Webhook response timeAny 2xx within 10 secondsThe delivery is retried; see Webhooks
Comment history stored per challenge6,000 commentsThe walk back through older history stops; newer comments keep being stored. See historyComplete on Comments and metrics on a challenge

Last updated on