CrawlBrain Docs
Pricing Support Open dashboardDashboard

Pages and Snippets API

These endpoints push content into a website's index without waiting for a crawl.

All endpoints need an Api-Key header and Pro. The base URL is https://api.crawlbrain.com/v1.

Pages versus snippets

Pages are real URLs on your site. Adding one tells CrawlBrain to fetch and index that URL now, instead of waiting for the nightly crawl.

Snippets are content that has no public page. You supply the text yourself. Use them for anything that exists in your database but not on a URL, such as FAQ answers or policy details.

Both end up in the same answer pool.

Add a page

POST /page/add
Field Type Required Notes
id string yes Website id
url string yes Must be http or https
curl -X POST https://api.crawlbrain.com/v1/page/add \
  -H "Api-Key: cb_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"id":"9f2c1a4e...","url":"https://example.com/new-product"}'

CrawlBrain fetches the URL and indexes its text. Once the website has been synced at least once, the page is available in answers within a minute or two. Before the first sync it is indexed with the rest of the site.

The URL is normalised before it is stored: it is lowercased, and the fragment and tracking parameters such as utm_* and gclid are removed. Use the same form when you later call /page/save or /page/delete.

Error Status Meaning
page_already_exists 409 The URL is already indexed. Use /page/save
no_credits 402 The credit balance is empty
website_not_found 404 No website with that id on this account

Fire this from your CMS's publish hook and new content answers immediately instead of tomorrow.

Update a page

POST /page/save

It takes the same fields as /page/add and re-fetches a URL that is already indexed. Call it from your update hook.

It also brings back a page you removed with /page/delete. A URL that was never added returns 404 with page_not_found.

Delete a page

DELETE /page/delete
Field Type Required
id string yes
url string yes

This removes the page from the index and from answers. It stays removed through later syncs until you call /page/save to restore it.

List pages

GET /pages/list
Field Type Required Notes
id string yes
limit integer no 1 to 500, default 100
include_deleted integer no 1 also returns removed pages

The response lists crawled pages for that website, most recently updated first. Snippets are not included; use /website/synced?type=article for those.

{
  "success": true,
  "count": 1,
  "pages": [
    {
      "url": "https://example.com/pricing",
      "title": "Pricing",
      "language": "en",
      "http_code": 200,
      "processed": 1,
      "ignore": 0,
      "updated_at": "2026-10-07 09:12:44"
    }
  ]
}

processed is 1 once the page has been fetched. ignore is 1 for removed pages.

Add a snippet

POST /snippet/add
Field Type Required Notes
id string yes Website id
url string yes Unique identifier for this snippet. Also where the source link points
title string yes Max 255 characters
content string yes Max 50,000 characters
type string no article (default), product, or your own label. webpage fetches the URL instead of using content
language string no Two-letter code, default en. An unsupported code is stored as en
curl -X POST https://api.crawlbrain.com/v1/snippet/add \
  -H "Api-Key: cb_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "9f2c1a4e...",
    "url": "https://example.com/faq#shipping-canada",
    "title": "Do you ship to Canada?",
    "content": "Yes. Standard delivery is 5 to 8 business days, free over $80.",
    "type": "article",
    "language": "en"
  }'

The url and title decide how useful a snippet is:

The url is the identity. It is unique per website and it is what a visitor clicks in the sources list. Use a real anchor on a real page where you can, and a stable synthetic URL where you cannot.

The title should be the question. Do you ship to Canada? retrieves far better than Shipping, because titles are matched against the visitor's question and visitors phrase things as questions.

A URL that is already taken returns 409 with snippet_already_exists. Use /snippet/save for that snippet instead.

Indexing a snippet costs credit. With an empty balance the call returns 402 with no_credits. A URL that matches an excluded path in Sync Settings returns 422 with excluded_url.

Update a snippet

POST /snippet/save
Field Type Required
id string yes
url string yes
title string yes
content string yes
language string no

This endpoint only updates. It will not create a missing snippet: the call returns 404 with snippet_not_found, and the type of an existing snippet cannot be changed.

Delete a snippet

DELETE /snippet/delete
Field Type Required
id string yes
url string yes

The call returns 404 with url_not_found when that website has no snippet at the URL.

Products

Shops can send exact product data instead of leaving CrawlBrain to read it from the page. The official shop plugins do this for you.

POST /products/sync
Field Type Required Notes
id string yes Website id
products array yes Up to 100 products per call

Each product:

Field Type Notes
url string Required. The product page on the website
title string Required
price number The current price, sale included
regular_price number The price before a sale. Ignored unless higher than price
currency string Three-letter code, such as USD
in_stock boolean
image string Image URL
sku string
rating, review_count number
categories array Category paths such as Men > Shirts > Linen. The deepest one is used
attributes object Up to 12 filters, such as {"Color": ["Blue", "White"], "Size": ["M", "L"]}
description string Short description
{ "success": true, "saved": 2, "skipped": [{ "url": "https://other.example/x", "reason": "other_host" }], "total": 148 }

This data drives product cards, sale and price ranking, stock, and the category and attribute filters in search. It takes priority over what the crawler reads from the page for 30 days after each sync, so sync again whenever a product changes. It costs no credit. A website holds at most as many products as its page limit; extra products are skipped with limit_reached.

The product page text is still indexed by the crawler, or by /page/add, for AI answers.

DELETE /products/delete      { "id": "...", "urls": ["https://example.com/product/old"] }

This removes products the shop no longer sells.

Upsert without guessing

There is no combined endpoint, so try /snippet/add first and fall back to /snippet/save when it returns 409:

async function upsertSnippet(payload) {
  const headers = {
    'Api-Key': process.env.CRAWLBRAIN_KEY,
    'Content-Type': 'application/json',
  };
  const base = 'https://api.crawlbrain.com/v1';

  let res = await fetch(`${base}/snippet/add`, {
    method: 'POST', headers, body: JSON.stringify(payload),
  });

  if (res.status === 409) {
    res = await fetch(`${base}/snippet/save`, {
      method: 'POST', headers, body: JSON.stringify(payload),
    });
  }

  return res.json();
}

Rate limits

Every write on this page is in the expensive bucket: 20 requests per minute. Each one dispatches an embedding job.

For a bulk import, sleep between calls rather than firing in a loop:

for (const item of items) {
  await upsertSnippet(item);
  await new Promise(r => setTimeout(r, 3200));   // ~19/min
}

For a one-off import of existing material, the document import screen is faster and has no rate limit to respect.

Credit

Adding a page or snippet is paid from the account's credit balance, at the same price as a page synced by the crawler. With an empty balance, /page/add and /snippet/add return 402 with no_credits. Updates and deletes do not check the balance.

Patterns worth stealing

Publish hook. Map your CMS's publish event to POST /page/add and its update event to POST /page/save. Wire delete to DELETE /page/delete as well and the index is never stale.

Product catalogue sync. A nightly job reads your product database and upserts one snippet per product, with a question-shaped title such as Is the Acme Widget in stock? and the current answer. Stock and price stay accurate without touching your storefront.

Policy sync from a single source. Keep shipping and returns policy in one system and push it as snippets to every website in your account, so one update corrects every site.