API reference

One endpoint, JSON in and out. Authenticate with the key from your credit purchase.

Authentication

Send your key as a bearer token. Keys start with bas_.

Authorization: Bearer bas_xxxxxxxxxxxxxxxxxxxxxxxx

POST /api/v1/extract

Fetches url and returns the requested fields. Pass either fields (simple) or schema (a full JSON Schema). Set many: true to get a list of items back under data.items.

{
  "url": "https://example.com/products",      // required, http(s)
  "fields": {                                  // name → "type — description"
    "name": "string",
    "price": "number — price without currency",
    "tags": "string[]",
    "image": "url"
  },
  "many": true,                                // optional, default false
  "instructions": "Skip sponsored listings",   // optional, max 2000 chars
  "escalate": true                             // optional: re-run doubtful results on a stronger model
}

Field types

When you send your own schema, it must use the JSON Schema subset the model supports: every object needs "additionalProperties": false, make fields nullable with anyOf, and leave out numeric constraints such as minimum/maximum and recursive schemas. Unsupported schemas return 400. The simple fields form always produces a valid schema.

Start a field description with one of string, number, integer, boolean, url, string[], number[]. Anything after it is a hint for the model. Every field can come back as null when it isn't on the page, and values are never invented.

Response

{
  "url": "https://example.com/products",
  "data": { "items": [{ "name": "…", "price": 19.99, "tags": ["…"], "image": "https://…" }] },
  "truncated": false,            // true if the page text exceeded the size limit
  "confidence": {                // per field, 0–1: probability the value is right
    "name": 0.98, "price": 0.95, "tags": 0.91, "image": 0.97
  },
  "escalated": false,            // true if verification triggered a re-run on the stronger model
  "usage": {
    "models": "claude-haiku-5-5+jev",
    "input_tokens": 8123,
    "output_tokens": 912,
    "credits": 3,
    "credits_remaining": 3997
  }
}

Confidence and escalation

After extraction, every field is checked against the page: is the value actually there, is it the right field, is it reasonable, does it match the format? confidence is the lowest of those checks for each field. In list mode, each field is scored across all items. If any check fails, the page is re-extracted on a stronger model (costs more credits; set escalate: false to opt out). confidence is null if verification is temporarily unavailable.

GET /api/v1/me

Returns your remaining balance.

{ "key_prefix": "bas_AbCdEf", "credits": 3997 }

Python example

import os, requests

r = requests.post(
    "https://bestaiscraper.com/api/v1/extract",
    headers={"Authorization": f"Bearer {os.environ['BAS_KEY']}"},
    json={"url": "https://news.ycombinator.com/", "many": True,
          "fields": {"title": "string", "url": "url", "points": "number"}},
    timeout=120,
)
r.raise_for_status()
for item in r.json()["data"]["items"]:
    print(item["points"], item["title"])

Limits

Pages are limited to 3 MB of HTML and about 100k characters of extracted text (longer pages are truncated and flagged). Up to 40 fields per request. Allow up to 90 seconds per request. JavaScript is not executed.

Errors

400Invalid body, URL or schema.
401Missing or invalid API key.
402Not enough credits for this page. Top up on the pricing page.
415The URL isn't HTML, JSON or text.
422The model couldn't produce a result (for example, the output was too large). Credits are refunded.
429Rate limited. Retry after a few seconds.
502The target page failed to load, or the model is temporarily unavailable. Credits are refunded.