HTTP API
The webfetch HTTP server — auth, endpoints, request/response schemas, error codes, and rate limits.
The HTTP API is a thin wrapper around the same core library that powers the CLI and the MCP server. Every endpoint mirrors an MCP tool. Request bodies validate via Zod; responses are JSON envelopes.
Try it: grab a free API key in 30 seconds at app.getwebfetch.com/signup, then point your requests at
https://api.getwebfetch.com.
#Base URL
- Self-hosted:
http://127.0.0.1:7600by default (--port/--hostoverride). - webfetch Cloud:
https://api.getwebfetch.com.
#Authentication
Every metered endpoint requires a bearer token:
Authorization: Bearer $WEBFETCH_KEY- Self-hosted: the server generates
~/.webfetch/server.tokenon first boot and prints it to stdout. - Cloud: your API key (prefix
wf_live_) from app.getwebfetch.com/keys.
Export the key once and paste any example below without editing it:
export WEBFETCH_KEY=wf_live_yourkeyMissing or invalid token → 401 Unauthorized.
#Response envelope
Every response is JSON with this shape:
{ "ok": true, "data": { } }On error:
{ "ok": false, "error": "human-readable reason" }#Endpoints
Metered webfetch Cloud API endpoints live under /v1. The public Cloud
utility endpoints, /providers and /health, remain unversioned; the
self-hosted local server uses the same unversioned paths for every endpoint.
#`POST /v1/search` — federated image search
Auth: bearer key required. Cost: 1 fetch unit.
Request body (query is the only required field):
{
"query": "drake portrait",
"providers": ["wikimedia", "openverse", "unsplash"],
"licensePolicy": "safe-only",
"maxPerProvider": 3,
"minWidth": 1200,
"minHeight": 800,
"safeSearch": "moderate",
"timeoutMs": 15000
}licensePolicy ∈ "safe-only" | "prefer-safe" | "any". safeSearch ∈ "strict" | "moderate" | "off".
Response data:
{
"candidates": [
{
"url": "https://upload.wikimedia.org/wikipedia/commons/...",
"thumbnailUrl": "https://...",
"sourcePageUrl": "https://commons.wikimedia.org/...",
"license": "CC_BY_SA",
"licenseUrl": "https://creativecommons.org/licenses/by-sa/4.0/",
"confidence": 0.95,
"author": "Jane Photog",
"authorUrl": "https://...",
"attributionLine": "Photo by Jane Photog / CC BY-SA 4.0",
"provider": "wikimedia",
"width": 2048,
"height": 1365,
"mime": "image/jpeg"
}
],
"providerReports": [
{ "provider": "wikimedia", "ok": true, "count": 12, "timeMs": 340 },
{ "provider": "openverse", "ok": true, "count": 8, "timeMs": 520 }
],
"warnings": []
}curl -sS -X POST https://api.getwebfetch.com/v1/search \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"drake portrait","licensePolicy":"safe-only"}'#`POST /v1/artist` — artist images by kind
Auth: bearer key required. Cost: 1 fetch unit.
{
"artist": "Taylor Swift",
"kind": "portrait",
"providers": ["spotify", "musicbrainz-caa", "wikimedia"],
"licensePolicy": "safe-only"
}kind ∈ "portrait" | "album" | "logo" | "performing". Defaults to "portrait". The adapter auto-picks the best providers per kind when providers is omitted.
Response shape is identical to /v1/search (candidates, providerReports, warnings).
curl -sS -X POST https://api.getwebfetch.com/v1/artist \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"artist":"Taylor Swift","kind":"portrait","licensePolicy":"safe-only"}'#`POST /v1/album` — canonical album artwork
Auth: bearer key required. Cost: 1 fetch unit.
{
"artist": "Radiohead",
"album": "In Rainbows",
"minWidth": 1000
}Returns the highest-resolution cover art available. Response shape: candidates, providerReports, warnings.
curl -sS -X POST https://api.getwebfetch.com/v1/album \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"artist":"Radiohead","album":"In Rainbows","minWidth":1000}'#`POST /v1/download` — fetch image bytes into the cloud cache
Auth: bearer key required. Cost: 2 fetch units.
{
"url": "https://upload.wikimedia.org/wikipedia/commons/...",
"maxBytes": 20971520
}maxBytes defaults to 20 MB (20,971,520). Maximum 100 MB. Only image/* MIME types are accepted. The worker fetches the URL server-side, SHA-256-hashes the bytes, stores them in R2, and returns the hash as a stable cache key — identical bytes from different URLs collapse to the same key.
Response data:
{
"url": "https://upload.wikimedia.org/wikipedia/...",
"sha256": "4a9f3b...",
"mime": "image/jpeg",
"byteSize": 482019,
"cacheKey": "4a9f3b..."
}curl -sS -X POST https://api.getwebfetch.com/v1/download \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg"}'#`POST /v1/probe` — list every image on a page with per-image license
Auth: bearer key required. Cost: 1 fetch unit.
{
"url": "https://example.com/article",
"respectRobots": true
}respectRobots defaults to true. Returns an array of every image found on the page, each with an inferred license tag and confidence score.
curl -sS -X POST https://api.getwebfetch.com/v1/probe \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://en.wikipedia.org/wiki/Drake_(musician)","respectRobots":true}'#`POST /v1/license` — determine license for a specific URL
Auth: bearer key required. Cost: 1 fetch unit.
{
"url": "https://upload.wikimedia.org/wikipedia/commons/...",
"probe": false
}probe: true also downloads and caches the image bytes (costs an additional R2 write). probe: false (default) is metadata-only and faster.
Response data:
{
"license": "CC_BY_SA",
"confidence": 0.95,
"author": "Jane Photog",
"attributionLine": "Photo by Jane Photog / CC BY-SA 4.0",
"sourcePageUrl": "https://commons.wikimedia.org/...",
"mime": "image/jpeg",
"sha256": "4a9f3b...",
"byteSize": 482019
}curl -sS -X POST https://api.getwebfetch.com/v1/license \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg","probe":false}'#`POST /v1/similar` — reverse-image search
Auth: bearer key required. Cost: 3 fetch units. Plan: Pro or above.
{
"url": "https://example.com/mystery.jpg",
"providers": ["serpapi", "brave"]
}providers is optional; omit to use all configured reverse-search providers. Requires at least one reverse-search provider key to be active on the platform side. Returns candidates, providerReports, warnings.
curl -sS -X POST https://api.getwebfetch.com/v1/similar \
-H "Authorization: Bearer $WEBFETCH_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg"}'#`GET /providers` — list configured providers
Auth: none required on webfetch Cloud. Self-hosted servers require
Authorization: Bearer <token>.
Returns all known provider IDs, the default provider set, which platform-managed provider keys are active on the Cloud side, and the metered endpoint list. When called with a valid session cookie the response also includes userPlan.
Response data:
{
"all": ["wikimedia", "openverse", "unsplash", "pexels", "pixabay", "itunes",
"musicbrainz-caa", "spotify", "youtube-thumb", "brave", "bing",
"serpapi", "browser", "flickr", "internet-archive", "smithsonian",
"nasa", "met-museum", "europeana", "library-of-congress",
"wellcome-collection", "rawpixel", "burst", "europeana-archival"],
"defaults": ["wikimedia", "openverse", "itunes", "musicbrainz-caa",
"unsplash", "pexels", "pixabay", "spotify", "brave",
"internet-archive", "smithsonian", "nasa", "met-museum",
"flickr", "europeana", "library-of-congress",
"wellcome-collection", "rawpixel", "burst"],
"platformProvidersAvailable": true,
"userPlan": null,
"endpoints": [
"/v1/search", "/v1/artist", "/v1/album", "/v1/download",
"/v1/probe", "/v1/license", "/v1/similar"
]
}curl -sS https://api.getwebfetch.com/providers#`GET /health` — liveness probe
Auth: none required on webfetch Cloud. Self-hosted servers require
Authorization: Bearer <token>.
curl -sS https://api.getwebfetch.com/healthResponse:
{ "ok": true, "service": "webfetch-api", "env": "production" }#Pricing & quota
| Plan | Price | Fetches / period | Period | Rate limit | /v1/similar |
|---|---|---|---|---|---|
| Free | $0 | 100 | daily | 10 req/min | no |
| Pro | $19/mo | 10,000 | monthly | 100 req/min | yes |
| Team | $79/mo | 50,000 | monthly | 300 req/min | yes |
| Enterprise | contact us | 1,000,000 | monthly | 1,000 req/min | yes |
Fetch unit costs by endpoint:
| Endpoint | Units |
|---|---|
/v1/search, /v1/artist, /v1/album, /v1/probe, /v1/license |
1 |
/v1/download |
2 |
/v1/similar |
3 |
Free plan has a hard daily cap — no overage charges, requests are rejected when the cap is hit. Pro and Team plans charge $0.015 and $0.010 per unit respectively for usage above the included amount.
Manage your subscription and view live usage at app.getwebfetch.com/billing. Full pricing details at getwebfetch.com/pricing.
#Common errors
#401 — missing or invalid key
{ "ok": false, "error": "unauthenticated" }Confirm Authorization: Bearer wf_live_... is present and the key hasn't been revoked in app.getwebfetch.com/keys.
#402 — quota exhausted
{ "ok": false, "error": "quota exceeded" }Free tier: resets at the next daily boundary (UTC midnight). Paid tiers: upgrade or wait for the monthly billing cycle to roll over.
#403 — endpoint not available on your plan
{ "ok": false, "error": "forbidden" }/v1/similar requires Pro or above. Upgrade at app.getwebfetch.com/billing.
#429 — rate limit hit
{ "ok": false, "error": "rate limit exceeded" }The response includes X-RateLimit-Reset (Unix seconds). Back off and retry after that timestamp. Limits are per API key.
#400 — validation error
{ "ok": false, "error": "query: Required" }The error message includes the Zod field path. Fix the request body and retry.
#415 — non-image content type (download only)
{ "ok": false, "error": "disallowed content-type: text/html" }/v1/download accepts only image/png, image/jpeg, image/webp, image/gif, image/avif, and image/svg+xml.
#500 — internal error
{ "ok": false, "error": "search failed" }Retry once. If the error persists, open an issue.
#Full example: Node.js / TypeScript
const r = await fetch("https://api.getwebfetch.com/v1/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.WEBFETCH_KEY}`,
},
body: JSON.stringify({
query: "kyoto temple",
licensePolicy: "safe-only",
minWidth: 1600,
}),
});
const { ok, data, error } = await r.json();
if (!ok) throw new Error(error);
console.log(data.candidates[0]);#Running the server (self-hosted)
bun run --cwd packages/server start
# or, Docker:
docker run -p 7600:7600 ghcr.io/ashlrai/webfetch server --host 0.0.0.0 --no-openSee Self-hosting for production deployment.