There are four programmatic surfaces:
- The
/api/v1REST API, authorized by a content API key. Projects, articles, article generation, publishing and the latest audit for whatever the key can see. - The MCP server at
/api/mcp, which exposes the same operations to an AI assistant, plus three public lookups. See the guide. - Keyed RSS feeds at
/api/v1/feed.xml, which syndicate your generated articles to anything that consumes a feed. - The free-tool endpoints (
/api/free/*) that back the public free tools. No key, no workspace data, cross-origin allowed.
Get a key
Open a project, go to Publishing, and create a content API key under the pull API section. The plaintext sbk_... shows once and is stored only as a hash. Send it on every request as an X-API-Key header or as Authorization: Bearer sbk_....
A key is minted for one project and sees exactly that project's data. Revoke it from the same page; a revoked key gets 401 on the next call, indistinguishable from a key that never existed.
The endpoints
All under https://searchblueprint.io/api/v1. JSON in, JSON out. Ids are uuids. An id outside the key's scope is a 404, the same as a missing one.
| Method and path | Does |
|---|---|
GET /projects | The projects the key can see, each with latest_grade from its last finished audit |
GET /projects/{id}/audit | The latest finished audit as { audit }: score, grade, verdict, totals, categories, recommendations, audit_running. audit: null when none has finished |
GET /projects/{id}/rankings | Retired on 2026-09-17. Answers 410 Gone; the keywords you track and their history are on each website's Rankings page |
GET /projects/{id}/integrations | Publishing connections: id, provider, status, auto_publish_status (the draft-vs-live default) |
GET /articles | Article summaries, newest first. ?limit= (1 to 50, default 20) and ?offset= |
GET /articles/{id} | One article with content_markdown and content_html |
POST /articles | Start writing one. Body { "keyword": "...", "title"?: "...", "brief"?: "...", "projectId"?: "..." }. Answers 202 with the job |
GET /jobs/{id} | Where a generation stands: status, queue_ahead, article_id once done, error if failed |
POST /articles/{id}/publish | Publish through one connection. Body { "integrationId": "...", "status"?: "draft" | "publish" } |
GET /feed.xml | RSS of the key's articles. A feed reader that cannot send a header uses the feed URL on Publishing > Developer: its ?token=sbf_... reads this feed and nothing else, and stops working when the key is revoked. Never put the sbk_ key itself in a URL |
projectId on POST /articles is optional for a project key (it has exactly one project).
Article fields (id, title, slug, meta_description, excerpt, seedKeyword, keywords, languageCode, hero_image_url, orgWebsite, created_at, plus content_markdown and content_html on the single-article call) follow the BabyLoveGrowth contract, so a blog package built for that API consumes ours unchanged. hero_image_url is null: we do not store one, and an empty field beats a made-up one.
Write and publish an article
Start it:
curl -X POST https://searchblueprint.io/api/v1/articles \
-H "X-API-Key: sbk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"keyword":"bridal gown alterations singapore","brief":"Brides 6 to 8 weeks out. Mention turnaround times."}'
{ "job_id": "8f1c...", "status": "queued", "keyword": "bridal gown alterations singapore", "article_id": null, "queue_ahead": null, ... }
Generation is long-running. The article-jobs cron drafts, scores and revises across several two-minute sweeps, so poll:
curl https://searchblueprint.io/api/v1/jobs/8f1c... -H "X-API-Key: sbk_YOUR_KEY"
When status is done, article_id is set. Fetch it from GET /articles/{article_id}, or publish it:
curl -X POST https://searchblueprint.io/api/v1/articles/ARTICLE_ID/publish \
-H "X-API-Key: sbk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"integrationId":"INTEGRATION_ID"}'
{
"sent": true,
"requested": "draft",
"external_url": "https://example.com/?p=145",
"error": null,
"event": { "status": "success", "publish_status": "draft", "url": "...", "verify_status": "skipped", "verify_http_status": null, "created_at": "..." },
"state": "unverified"
}
Read state, not sent. sent says the CMS accepted the write. state is what the Publishing tab shows: published only after the product fetched the public URL and found the article on it; checking while the verifier waits for the host to deploy; not_live when the URL answered 404 past the grace window; unverified for a draft or a destination with no public URL; failed when the write itself failed (then error says why). Leaving status out uses the connection's own draft-vs-live setting.
Status codes
401no key, or a revoked one.400a missingkeyword, a malformed id, astatusthat is notdraftorpublish, or a keyword the writer refuses (a test string, a bare brand name).402the workspace is out of article credits.404the project, article, job or connection is not in the key's scope.409the project already has an article for that keyword (the body carriesexisting_article_id), or the keyword is already being written.422the publish gate blocked the article: unfilled placeholders, refusal text, more than one H1. The body listsissues.429rate limited, with aRetry-Afterheader.
Limits and usage
- Reads: 120 requests per 10 minutes per IP address across
/api/v1. POST /articlesandPOST /articles/{id}/publish: a further 20 per 10 minutes per key. Each one spends credits or writes to a customer's CMS.- Free-tool endpoints (
/api/free/*) have their own, tighter, anonymous buckets. Sustained extraction gets throttled. - Responses are the JSON the product's own pages consume. Fields are stable in practice but not versioned: pin your integration to the fields you use and tolerate additions.
Call a free endpoint
To call a free-tool endpoint from your own code:
- Open a free tool in the browser with dev tools on, run it, and read the request. The endpoint shape is exactly what you saw.
- Call the same endpoint from your code with the same JSON body.
- Handle
429by slowing down. A minute later, the bucket is full again.
No authentication means no access to any workspace's data. The free endpoints only compute over what you send them.
If something isn't working
Everything answers 401. The header name is X-API-Key (or Authorization: Bearer), and the key starts with sbk_. A key created for one project does not open another; make a key on the project you are reading.
POST /articles answers 409 with existing_article_id. The project already has an article for that keyword. Refresh that one in the product instead of writing a competing page.
POST /articles/{id}/publish answers 404 for a connection you can see in the product. The connection belongs to a different project than the article. GET /projects/{id}/integrations lists the ones that match.
An endpoint's response shape changed. Unversioned endpoints evolve with their tools. The current shape is always observable by calling it.