developers

A key, an install, one call.

Three steps to the first page, and the same envelope for everything after it — id, kind, status, progress, creditsUsed, data, next — with cursor pagination, Idempotency-Key, X-Request-Id and X-MeshArc-Credits on every response, typed error codes, and an OpenAPI spec committed with every route change.
Get an API keyTry a URL in the playgroundOpenAPI at /docs on the API host
01quickstart

The first page in under a minute.

step 1

Make a key

Settings → API keys. Pick a scope, optionally a project list and an expiry. Sign up first if you have not — 1,000 credits, no card.

the key never leaves your environment
export MESHARC_KEY=mesharc_…   # from Settings → API keys
step 2

Read one page

/scrape with one url waits for the page and returns it. maxCredits caps what it may cost; the ladder does the rest.

POST /v1/scrape
curl -X POST https://api.mesharc.dev/v1/scrape \
  -H "Authorization: Bearer $MESHARC_KEY" \
  -d '{ "url": "https://www.gov.uk/news", "formats": "markdown", "maxCredits": 4 }'
step 3

Read the response

The page, its verdict, the mode that read it and what it cost — and the same on the X-MeshArc-Credits header, so a proxy can meter without parsing.

200 · one page, one credit
HTTP/1.1 200 OK
X-MeshArc-Credits: 1
X-Request-Id: 01J9…

{
  "id": "6d1c…", "kind": "scrape", "status": "done", "creditsUsed": 1,
  "data": [{
    "url": "https://www.gov.uk/news", "verdict": "ok",
    "method": "tls", "tier": "http", "credits": 1,
    "markdown": "# News and communications\n…"
  }],
  "next": null
}
then

Crawl a site, keep it

/crawl makes an ephemeral project, kept a day unless /keep promotes it to a standing one with a name and a schedule. From then on every run is diffed against the last.

POST /v1/crawl → /keep
curl -X POST https://api.mesharc.dev/v1/crawl \
  -H "Authorization: Bearer $MESHARC_KEY" \
  -H "Idempotency-Key: gov-news-2026-09-18" \
  -d '{ "url": "https://www.gov.uk/news", "limit": 500, "useSitemap": true,
        "webhook": { "url": "https://hooks.example/mesharc", "events": ["crawl.completed"] } }'

# 202 · poll GET /v1/crawl/{id}?cursor=… until status is done
# then POST /v1/crawl/{id}/keep  { "name": "gov.uk news", "schedule": "daily" }
/scrape
One url waits (60 s by default, 120 at most) and returns the page; urls[] is a batch, always 202.
/crawl
A one-shot crawl, paged by cursor; DELETE cancels; /keep makes it a project.
/map
Every URL a site declares, 1 credit per sitemap file read — most sites are one file. Synchronous when the tree resolves in ten seconds, a job otherwise.
02the API

Projects, runs, pages, changes, destinations — under one workspace.

methodpathwhat
POST/v1/scrapeOne URL synchronously, or urls[] as a batch. maxCredits caps a single page.
POST/v1/crawlA one-shot crawl of an ephemeral project.
POST/v1/crawl/{id}/keepPromote that crawl to a standing project.
POST/v1/mapThe site's URLs from its sitemap. 1 credit per sitemap file read.
POST/v1/projectsCreate one project or a batch.
PATCH/v1/projects/{id}Configure scope, formats, steps, schedule, tier.
POST/v1/projects/{id}/dry-runResolve sources and sample URLs without spending.
GET/v1/projects/{id}/discoveryThe stored sitemap tree and its sections.
POST/v1/projects/{id}/runsStart a run. GET to list, DELETE to cancel.
GET/v1/projects/{id}/pagesList, search, fetch content, recrawl, screenshot.
GET/v1/projects/{id}/changesList, review, page diff, markup diff.
GET/v1/projects/{id}/destinationsDestinations, shapes and sync history.
POST/v1/projects/{id}/exportA dataset as CSV or JSONL, per run or per project.
GET/v1/webhooks/deliveriesWhat a job or project webhook was sent, attempt by attempt. POST …/{id}/redeliver sends one again.
GET/v1/mePlan, limits, credits. Keys, usage, billing, proxies, monitor, members.
error codes
  • validation
  • unauthorized
  • not_found
  • rate_limited
  • credits_exhausted
  • plan_limit
  • tier_limit
{ "error", "code", "request_id" } · never a stack trace · another workspace’s resource is a 404, not a 403
keys and limits
  • scopesread · write · admin — mapped onto the role ladder
  • projectsa key may be restricted to some projects; the rest are 404
  • expiryan expired key is refused, not quietly downgraded
  • rate limitper key, a minute’s window; 429 with Retry-After and X-RateLimit-*
concepts
  • ProjectA site (seed URL) plus its configuration: scope, sources, schedule, formats, steps, tier. Lives in a workspace.
  • RunOne crawl of a project. Pages, a stop reason, counts, a credit tally, a sitemap snapshot, a link graph, a change record against the previous run.
  • PageOne URL in one run: markdown / text / HTML bodies, head metadata, structured fields, a verdict, the mode it was read in, the tier, the cost.
  • ChangePer page of a run: added / removed / modified / same / withheld / unverified / skipped, plus field-level changes and selector-break signals.
  • SourceWhere a project's URLs come from: the seed and its links, the sitemap tree by section, URL lists, feeds, patterns.
  • DestinationA place rows are pushed after each run, with a declarative shape (filter / map / drop).
03webhooks and agents

Signed events for every change, and seventeen tools for an agent.

a deliveryHMAC-SHA256
POST https://hooks.example/mesharc
X-MeshArc-Event: page.changed
X-MeshArc-Delivery: 8f2a…
X-MeshArc-Signature: sha256=<hmac-sha256 of the raw body, with the project's secret>

{ "event": "page.changed", "project": { "id": "…", "host": "www.gov.uk" },
  "run": { "id": "…" }, "data": { "url": "…", "kind": "modified", "fields": ["title", "published_at"] },
  "ts": "2026-09-18T06:30:00Z" }
verify it — python
import hmac, hashlib

def verify(secret: str, body: bytes, header: str) -> bool:
    want = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, header)
events
page
  • page.changed
  • page.blocked
  • page.orphaned
  • page.linked
  • field.canonical
site
  • site.failing
  • site.recovered
  • run.finished
sitemap
  • sitemap.added
  • sitemap.removed
  • sitemap.updated
  • sitemap.file_added
  • sitemap.file_removed
job
  • crawl.started
  • crawl.page
  • crawl.completed
  • crawl.failed
  • scrape.completed
  • scrape.failed
  • map.completed
  • batch.finished
Project-scoped or job-scoped (the webhook field of /crawl and /scrape). A test button, secret rotation, a delivery log; six retries over a day, then paused and said so. crawl.page is batched fifty at a time.
MCP server — Claude Desktop, Cursor, Claude Codestdio
pip install "mesharc[mcp]"
claude mcp add mesharc -e MESHARC_API_KEY=… -- mesharc-mcp
the tools an agent getsthe MCP page → · 17 tools
  • scrape_urls
  • extract_url
  • map_site
  • crawl_site
  • search_pages
  • get_page
  • keep_crawl_as_project
  • create_project
  • describe_project_config
  • get_project
  • update_project
  • list_projects
  • start_run
  • list_pages
  • get_changes
  • recrawl_pages
  • get_job
formats, steps, datasets
markdowntextrawHtmlcleanHtmllinksrawscreenshotjson
clicktypescrollwaitpressselecteachclick … until gone
pagesmarkdownchangesfieldssitemapllms.txtllms-full.txt
Formats per page · browser steps before a page is read, scoped to every page, the start page or path globs · export datasets as CSV or JSONL, or a site as llms.txt and llms-full.txt.

Both SDKs are thin on purpose: every method is one API call, or a poll loop where wait is on, and returns the API’s JSON — so the OpenAPI spec is the reference and the SDK never disagrees with it.

04what a page carries

A page row says what it is, how it was read, and what it cost.

verdict · shape · warnings

ok · thin · blocked · skipped, with the typed reason. A short page is ok when its markup says what it is (listing, table, form), and carries a short warning otherwise — an errorCode (BLOCKED, NOT_FOUND, TIER_LIMIT, CAPTCHA, LOGIN_REQUIRED …) is for what went wrong.

method · tier · credits

The mode that answered, its tier, and what it cost. A refusal records how far up the ladder it climbed.

fields

Structured fields filled from what the page already declares — head, Open Graph, JSON-LD, microdata — or by your model on your key.

bodies

markdown, text, cleanHtml, rawHtml, links, a screenshot; documents parsed to text.

widget · solved

A captcha widget that stood on the page, and whether it was clicked past, answered, or left alone.

exitBytes

What a residential render pulled through the exit, so the surcharge is read off the row.

inSitemap · orphan · inLinks

Whether the sitemap declared the page, and how many pages link to it.

origin

For a copy carried forward at zero credits, the run that actually fetched it.

05the details

What you would otherwise look up in the spec.

How do I authenticate?

Authorization: Bearer <key>. Keys are made under Settings with a scope (read, write or admin), an optional project allow-list, an expiry and their own requests-per-minute. A key restricted to some projects sees the others as 404, not 403 — existence is never confirmed across the line.

Is /scrape synchronous?

One url waits for its page — 60 seconds by default, 120 at most — and returns it in the same response. Pass urls[] and it is a batch, always 202 with a job to poll. Either way the same envelope comes back: id, kind, status, progress, creditsUsed, data, next.

What does Idempotency-Key do?

Send it on any POST and a retry within 24 hours returns the first answer instead of starting a second job. The key is scoped to the route it was first used on. Batch endpoints accept one per item.

How does pagination work?

Every list takes limit (up to 1,000) and cursor, and answers with next — a ready-made URL, or null when the list is done. A job’s pages page the same way, so a 200,000-page crawl is read in slices without holding the lot.

How are webhooks signed and retried?

Every delivery carries X-MeshArc-Signature: sha256=<HMAC-SHA256 of the raw body with the project’s secret>, plus X-MeshArc-Event and X-MeshArc-Delivery. Answer 2xx within the timeout; anything else is retried six times over about a day (1 m, 5 m, 30 m, 2 h, 6 h, 12 h), then paused and shown as such with its log.

What happens at the rate limit?

A 429 with Retry-After and X-RateLimit-Limit, -Remaining and -Reset on every response. The window is a minute, per key; the plan sets the default and a key can be given a lower one.

1,055 selftest cases · OpenAPI spec committed with every route change

Make the first call.

A one-time 1,000 credits, browser tier included, no card. The response says the mode it took and what it cost.