Skip to content
Quickstart
Documentation

Quickstart

One API key and one JSON envelope for 73 social, e-commerce and web data endpoints. Only successful calls that return data are charged.

Three steps to a first call

  1. Create an API key

    Create a key in the console and, if you like, limit the endpoints it can call (scopes). The full key is shown once, when you create it, so save it right away: the server keeps only a hash and cannot show it again.

    Shell
    export QOVIA_KEY="sk-qovia-…"

    Keys look like sk-qovia- plus 43 letters and digits.Create an API key

  2. Check the keyGET/v1/account

    Confirms the API key works and returns balance, plan, per-minute limit and this month's free quota. Free, but counts toward the rate limit.

    cURL
    curl https://api.qovia.ai/v1/account \
      -H "Authorization: Bearer $QOVIA_KEY"
    Example response
    {
      "request_id": "00000000-0000-0000-0000-000000000000",
      "data": {
        "user": {
          "id": "00000000-0000-0000-0000-000000000000",
          "email": "you@example.com"
        },
        "api_key": {
          "id": "00000000-0000-0000-0000-000000000000",
          "name": "production",
          "scopes": [
            "*"
          ],
          "expires_at": null
        },
        "billing": {
          "balance_usd": "10.000000",
          "plan": "payg",
          "rpm_limit": 600,
          "free_quota": {
            "limit": 1000,
            "used": 12,
            "remaining": 988,
            "resets_at": "2026-11-01T00:00:00+08:00"
          }
        }
      },
      "meta": {
        "billed": false
      }
    }
  3. Make your first call

    Try Bilibili video search (GET /v1/raw/bilibili/search): it serves live data and runs on the free quota.

    curl 'https://api.qovia.ai/v1/raw/bilibili/search?keyword=%E5%8F%B0%E7%A7%AF%E7%94%B5' \
      -H "Authorization: Bearer $QOVIA_KEY"

    Replace $QOVIA_KEY with your API key

    Example response200 OK
    {
      "request_id": "00000000-0000-0000-0000-000000000000",
      "data": {
        "items": [
          {
            "id": "soc_bil_2913ac9c522a",
            "src": "social",
            "platform": "bilibili",
            "lang": "zh-CN",
            "text": "「小白」全球芯片代工王!万字台积电发展史",
            "author": {
              "uid_hash": "ca5fa000e345",
              "mid": "8969156",
              "name": "小白测评"
            },
            "metrics": {
              "plays": 689248,
              "likes": 20365,
              "saves": 7230,
              "comments": 1348,
              "danmaku": 2676
            },
            "extra": {
              "source_id": "BV1DkoeYhExZ",
              "url": "https://www.bilibili.com/video/BV1DkoeYhExZ",
              "title": "「小白」全球芯片代工王!万字台积电发展史",
              "published_at": "2025-03-26T18:30:00+08:00",
              "category": {
                "id": 95,
                "name": "数码"
              },
              "tags": [
                "芯片",
                "数码",
                "台积电",
                "手机芯片"
              ],
              "cover": "https://i0.hdslb.com/bfs/archive/8f62d780e4e308bafa8c9f6c4d14ce5a28b2ce0b.jpg"
            },
            "prov": {
              "hash": "0xc16f694e973143c1",
              "ts": "2026-10-20T12:00:00+08:00",
              "source": "live"
            }
          }
        ],
        "cursor": 2,
        "has_more": true
      },
      "meta": {
        "billed": false,
        "charged_usd": "0.000000",
        "used_free_quota": true,
        "free_quota_remaining": 999,
        "balance_usd": "0.000000",
        "empty": false
      }
    }

    Catalog sample, not live data

    A new account's first 1,000 calls run on the free quota and cost nothing; after that the same call is charged at its price, as the meta of the paid example under Responses below shows.

    See this endpoint's full reference

Base URL and authentication

Every endpoint lives under one base URL:

https://api.qovia.ai

Send the API key with every request, as a Bearer token in the Authorization header:

HTTP
Authorization: Bearer $QOVIA_KEY
  • You can send x-api-key: <API Key> instead; when both are present, Authorization wins.
  • The key is read from these two headers only, never from the URL or a cookie.
  • A missing, malformed, revoked or expired key gets 401 invalid_api_key; 30 failed attempts from one IP within a minute get 429 too_many_auth_failures.

Scopes

A key carries one or more scopes, picked when you create it; calling an endpoint outside them gets 403 scope_denied.

API key scopes
scopeGrants
*Every endpoint
searchCross-domain search
raw:*Every raw endpoint
raw:<platform>Every raw endpoint of one platform: raw:douyin, raw:bilibili, raw:xingtu, raw:tieba, raw:xiaohongshu, raw:tiktok, raw:weibo, raw:shopee, raw:amazon, raw:fliggy, raw:news, raw:cljx, raw:huahuo

Requests

GET endpoints
Parameters go in the query string and are converted to their declared types: integers as digits, booleans as true / false / 1 / 0 / yes / no, and arrays by repeating the name (?id=1&id=2).
POST endpoints
The body is one JSON object, sent with Content-Type: application/json. A body that is not valid JSON, or not an object, gets 400 invalid_params.
Validation
Only the listed parameters are accepted. An unknown one, a missing required one, or a wrong type or format gets 400 invalid_params, not charged. Its message starts with the parameter's name (item_id: …); a missing or unknown parameter starts it with params: ….
Defaults
Optional parameters you leave out get their defaults on the server (see each parameter's Default).

Responses

A success is HTTP 200 with the shared envelope. Here is a paid call, once the free quota is used up; compare it with step 3:

Example response200 OK
{
  "request_id": "00000000-0000-0000-0000-000000000000",
  "data": {
    "items": [
      {
        "id": "soc_bil_2913ac9c522a",
        "src": "social",
        "platform": "bilibili",
        "lang": "zh-CN",
        "text": "「小白」全球芯片代工王!万字台积电发展史",
        "author": {
          "uid_hash": "ca5fa000e345",
          "mid": "8969156",
          "name": "小白测评"
        },
        "metrics": {
          "plays": 689248,
          "likes": 20365,
          "saves": 7230,
          "comments": 1348,
          "danmaku": 2676
        },
        "extra": {
          "source_id": "BV1DkoeYhExZ",
          "url": "https://www.bilibili.com/video/BV1DkoeYhExZ",
          "title": "「小白」全球芯片代工王!万字台积电发展史",
          "published_at": "2025-03-26T18:30:00+08:00",
          "category": {
            "id": 95,
            "name": "数码"
          },
          "tags": [
            "芯片",
            "数码",
            "台积电",
            "手机芯片"
          ],
          "cover": "https://i0.hdslb.com/bfs/archive/8f62d780e4e308bafa8c9f6c4d14ce5a28b2ce0b.jpg"
        },
        "prov": {
          "hash": "0xc16f694e973143c1",
          "ts": "2026-10-20T12:00:00+08:00",
          "source": "live"
        }
      }
    ],
    "cursor": 2,
    "has_more": true
  },
  "meta": {
    "billed": true,
    "charged_usd": "0.001500",
    "used_free_quota": false,
    "free_quota_remaining": 0,
    "balance_usd": "9.998500",
    "empty": false
  }
}

Catalog sample, not live data

Success

Fields of a successful response
FieldTypeDescription
request_idstringThe call's ID (its usage record), also in the X-Request-Id header and in the console usage log.
dataobject | array | nullThe endpoint's data; see each endpoint's response example. An empty result may be an empty structure (e.g. empty items) or null; check meta.empty.
meta.billedbooleanWhether this call was charged to the balance; false for free-quota calls and empty results.
meta.charged_usdstringAmount charged in USD, 6 decimals; "0.000000" when nothing was charged.
meta.used_free_quotabooleanWhether this call used the free quota.
meta.free_quota_remainingintegerFree calls left this month.
meta.balance_usdstringBalance after settlement, USD, 6 decimals.
meta.emptybooleanThe result is empty: not charged, and a used free call is given back.
Empty results

An empty result is still HTTP 200 with meta.empty true, not charged (a used free call is given back); data may be an empty structure or null, so check meta.empty.

Errors

Fields of an error response
FieldTypeDescription
error.codestringThe error code; see the error table. Branch on code.
error.messagestringA short English explanation. One code can have several messages (the error table lists them all), so do not branch on it. Never contains the data source's own error text.
error.request_idstringSame as the X-Request-Id header (see below).

Response headers

Response headers
HeaderSent withDescription
X-Request-IdEvery response except an unexpected plain-text 500Same as request_id in the body. For a reserved call (success, empty, or a failure after reservation) it is the usage record ID; a request refused before reservation keeps the X-Request-Id you sent, or gets a generated one.
X-RateLimit-LimitSuccess (including empty)GET /v1/accountErrors: rate_limited, too_many_unbilled, upstream_busy, upstream_timeout, upstream_error, upstream_rejected, not_foundThe account's requests-per-minute limit.
X-RateLimit-RemainingSuccess (including empty)GET /v1/accountErrors: rate_limited, too_many_unbilled, upstream_busy, upstream_timeout, upstream_error, upstream_rejected, not_foundRequests left in the current 60-second window.
X-Qovia-ChargedSuccess (including empty)Errors: upstream_busy, upstream_timeout, upstream_error, upstream_rejected, not_foundCharged for this call, USD, 6 decimals; 0.000000 when nothing was charged or it was refunded.
X-Qovia-BalanceSuccess (including empty)Errors: upstream_busy, upstream_timeout, upstream_error, upstream_rejected, not_foundBalance after settlement, USD, 6 decimals.
Retry-AfterErrors: too_many_auth_failures, rate_limited, too_many_unbilled, endpoint_busy, upstream_busySeconds to wait before retrying.
WWW-AuthenticateErrors: invalid_api_keyBearer realm="qovia".
AllowErrors: method_not_allowedThe methods this path supports.

/v1 only takes GET and POST: other methods (e.g. PUT, DELETE) get a framework 405 {"detail": "Method Not Allowed"}, and an unexpected server error may be a plain-text 500. Neither is charged.

Billing and free quota

  • Only successful calls that return data are charged.
  • Failures, empty results and timeouts cost nothing: the reserved charge, or free call, is given back automatically.
  • 1,000 free calls a month, reset each calendar month in Asia/Singapore, on endpoints marked "Free quota applies"; free calls do not count toward the tiers.
  • When the balance cannot pay for a call, nothing runs and you get 402 insufficient_balance.
  • Every call reports its charge in meta and in the X-Qovia-Charged / X-Qovia-Balance headers.

Tiered prices

Each product is priced in marginal monthly tiers: within a calendar month, billed calls past a threshold are priced at the next tier.

Tiered prices per product
ProductBilled calls a monthPer 1kPer call
Search0 – 1M$4$0.004
Search1M – 10M$3$0.003
Search10M+$2.5$0.0025
Raw endpoints0 – 5M$1.5$0.0015
Raw endpoints5M+$1$0.001

Fixed-price endpoints

These endpoints are not tiered: every successful call costs a fixed price.

Fixed-price endpoints
PriceFree quotaEndpoints
$0.01 / call$10 per 1kNot usedXingtu (16), Xiaohongshu (11)
$0.0015 / call$1.5 per 1kNot usedCili Juxing (12), Huahuo (6)
See pricing

Rate limits

Free accounts
60 req/min
After the first top-up
600 req/min
Window
60 s, sliding
  • The per-minute limit is per account: all its API keys and the console playground share it.
  • Some endpoints also have a global per-second cap (across all users; see each endpoint's Rate cap); over it you get 429 endpoint_busy, and the request does not count toward your per-minute limit.
  • If, in the last 10 minutes, at least 50 of your calls to data sources, and over 80% of them, came back empty, not found or rejected, you get 429 too_many_unbilled; slow down and retry.
  • When a 429 or 503 carries a Retry-After header, wait that many seconds before you retry. A 503 endpoint_unavailable has none: the endpoint is not live, so retrying will not help.

Errors

An error comes with its HTTP status and the error envelope:

Error response429
{
  "error": {
    "code": "rate_limited",
    "message": "rate limit exceeded",
    "request_id": "00000000-0000-0000-0000-000000000000"
  }
}
Every error code
codeHTTPBillingDescription
invalid_api_key401Not chargedThe API key is missing, malformed, unknown, revoked or expired.
  • missing or invalid API key
  • API key has been revoked
  • API key has expired
too_many_auth_failuresRetryable429Not charged30 failed authentications from this IP within a minute; try again in 60 seconds.
  • too many failed authentications; try again later
account_disabled403Not chargedThe account is disabled.
  • account is disabled
endpoint_not_found404Not chargedNo such endpoint; check the method and path.
  • no endpoint <METHOD> <path>
method_not_allowed405Not chargedThe path exists but takes another method; the Allow header names it.
  • use <METHODS>
endpoint_disabled410Not chargedThe endpoint has been retired.
  • <id> has been retired
scope_denied403Not chargedThis API key lacks the scope the endpoint requires.
  • this API key does not have scope <scope>
endpoint_unavailable503Not chargedThe endpoint is not available right now (no data source connected yet, or no price configured); nothing ran.
  • <id> is temporarily unavailable
  • pricing for this endpoint is not configured
invalid_params400Not chargedThe parameters failed validation: a required one is missing, a type or format is wrong, an unknown one was sent, or the POST body is not a JSON object. An error in one parameter starts with its name (item_id: …); a missing or unknown parameter starts with params: ….
  • <param>: <reason>
  • params: <reason>
  • request body is not valid JSON
  • request body must be a JSON object
rate_limitedRetryable429Not chargedOver the account's per-minute limit (sliding 60-second window); wait for Retry-After.
  • rate limit exceeded
too_many_unbilledRetryable429Not chargedIn the last 10 minutes at least 50 of your calls to data sources, and over 80% of them, came back empty, not found or rejected; slow down.
  • too many recent requests came back empty, not found or rejected; slow down. Not charged
endpoint_busyRetryable429Not chargedThe endpoint's per-second capacity across all users is full; retry in 1 second. This does not count toward your per-minute limit.
  • <id> is at capacity; retry shortly
insufficient_balance402Not chargedThe balance cannot pay for this call (the free quota is used up, or the endpoint does not use it).
  • insufficient balance for this request
upstream_busyRetryable503RefundedThe data source is busy or cooling down; refunded. Retry after Retry-After.
  • upstream is busy; retry later; not charged
upstream_timeout504RefundedThe data source timed out; refunded.
  • upstream timed out; not charged
upstream_error502 / 503RefundedThe data source failed; refunded.
  • upstream failed; not charged
upstream_rejected400RefundedThe data source rejected these parameters (well-formed, but not accepted); refunded.
  • the upstream rejected these parameters; not charged
not_found404RefundedThe data source has no such item (deleted, or the ID does not exist); refunded.
  • no such item upstream; not charged
reservation_expired504RefundedThe call took too long and was refunded automatically; no data is returned.
  • the request took too long and was not charged

Branch on error.code; message is for people and can be worded several ways.

For AI agents and tools

AI tools can call these endpoints directly over MCP, and the same catalog is published in machine-readable form for code generators: