Documentation
WebVitalist API
Order audits for client sites from your own tools, poll for results and receive a signed webhook when a report is ready. The API is included in the Agency plan; every order is paid with your plan's credits.
Base URL: https://webvitalist.harald-sigvartsen.workers.dev/api/v1
Authentication
Create keys under Account → API. Keys look like wv_live_ followed by 32 characters; the full key is shown once. Send it as a Bearer
token on every request:
Authorization: Bearer wv_live_YOUR_KEY Requests without a valid key receive 401. Revoking a key takes effect
immediately. Keys are scoped to the account that created them and share its credits.
Credits & costs
The API never charges a card: each order debits credits from your subscription balance at
the same rate as the web app. With too few credits the request fails with 402 and nothing is created.
product | Name | Credits | Pages | Typical time |
|---|---|---|---|---|
audit | Audit | 1 | 1 | ~3 min |
deep | Deep Audit | 3 | 15 | ~6 min |
full | Full Test | 8 | 25 | ~12 min |
Fix Pack is not orderable through the API. Agency includes 60 credits per month ($199/mo, or $1,990/yr with the year's credits up front). If an audit fails on our side the credits are refunded automatically.
Create an audit
POST /api/v1/audits
| Field | Type | Description |
|---|---|---|
url | string, required | Public http(s) page to audit. Scheme optional; credentials and fragments are stripped. |
product | audit | deep | full | Defaults to audit. |
callback_url | string, optional | https URL on a public host. We POST a signed summary on completion or failure. |
options.goal | leads | sales | signups | traffic | other | Steers the AI copy and UX review. |
options.notes | string ≤ 1000 | Free-form context for the consultant prompt. |
options.platform_hint | string ≤ 60 | e.g. Shopify, WordPress, Next.js. |
curl -X POST https://webvitalist.harald-sigvartsen.workers.dev/api/v1/audits \
-H "Authorization: Bearer wv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"product": "deep",
"callback_url": "https://hooks.example.com/webvitalist",
"options": { "goal": "sales", "platform_hint": "Shopify" }
}' Returns 201 with the new audit; processing starts immediately.
HTTP/1.1 201 Created
{
"id": "3f9d1c2e-…",
"status": "queued",
"url": "https://example.com/",
"product": "deep",
"source": "api",
"credits_spent": 3,
"credits_remaining": 57,
"scores": null,
"created_at": "2026-09-13T12:00:00.000Z",
"completed_at": null,
"links": {
"self": "https://webvitalist.harald-sigvartsen.workers.dev/api/v1/audits/3f9d1c2e-…",
"live": "https://webvitalist.harald-sigvartsen.workers.dev/audits/3f9d1c2e-…?k=…",
"report": null,
"download": null
}
} Statuses are queued, processing, completed and failed. The links.live page can be shared with a client to watch
progress; report and download are populated once completed.
Get an audit
GET /api/v1/audits/{id} — only audits owned by the key's account.
curl https://webvitalist.harald-sigvartsen.workers.dev/api/v1/audits/3f9d1c2e-… \
-H "Authorization: Bearer wv_live_YOUR_KEY" {
"id": "3f9d1c2e-…",
"status": "completed",
"url": "https://example.com/",
"product": "deep",
"credits_spent": 3,
"scores": { "overall": 71, "technical": 82, "performance": 54, "accessibility": 88, "content": 70, "ux": 66 },
"progress": {
"done": 8, "total": 8, "running": null,
"steps": [ { "id": "fetch", "status": "done", "score": null, "summary": "…" }, … ]
},
"platform": { "cms": "Shopify", "framework": null, … },
"result": { "pagesCrawled": 12, "crawl": { "brokenLinks": 2, … }, … },
"error": null,
"links": {
"report": "…/report/3f9d1c2e-…?k=…",
"download": "…/report/3f9d1c2e-…/download?k=…"
}
} progress.steps lists every step of the product in order with pending, running, done, skipped or failed. Poll every 10–20 seconds, or use a callback instead. The download link returns the full report as Markdown (text/markdown);
it carries an access token, so treat it as a secret.
List audits
GET /api/v1/audits?limit=20&offset=0 — newest first, limit up
to 100. The response has data, limit, offset and has_more. Includes audits ordered on the web and by monitors, not only API
orders (see source).
curl "https://webvitalist.harald-sigvartsen.workers.dev/api/v1/audits?limit=20&offset=0" \
-H "Authorization: Bearer wv_live_YOUR_KEY"Account
GET /api/v1/me — email, plan (id, status, interval, period end), current credit balance,
the key in use and the product price list. Handy for a “credits left” widget.
curl https://webvitalist.harald-sigvartsen.workers.dev/api/v1/me \
-H "Authorization: Bearer wv_live_YOUR_KEY"Callbacks
When an order with a callback_url completes or fails, we POST a JSON summary
within seconds. Respond with any 2xx; on a non-2xx response or a timeout
(10 s) we retry once. Deliveries carry a Webvitalist-Delivery header ({audit id}:{attempt}) so you can de-duplicate.
POST https://hooks.example.com/webvitalist
Content-Type: application/json
Webvitalist-Event: audit.completed
Webvitalist-Delivery: 3f9d1c2e-…:1
Webvitalist-Signature: sha256=9b3f…
{
"id": "3f9d1c2e-…",
"status": "completed",
"url": "https://example.com/",
"product": "deep",
"scores": { "overall": 71, … },
"report_url": "…/report/3f9d1c2e-…?k=…",
"download_url": "…/report/3f9d1c2e-…/download?k=…",
"error": null,
"completed_at": "2026-09-13T12:06:41.000Z"
} Verifying the signature
The Webvitalist-Signature header is sha256= followed by the hex HMAC-SHA256
of the raw request body. The HMAC secret is the hex SHA-256 of the API key that created the order
— you can derive it from your key without storing anything extra. Always verify against the raw
bytes, before parsing the JSON.
import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
const secret = createHash('sha256').update(process.env.WEBVITALIST_KEY).digest('hex');
export function verify(rawBody, signatureHeader) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
return expected.length === signatureHeader.length &&
timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
} Callback targets must be https on a public hostname. We follow no redirects. If a delivery
fails twice the report is still available via GET /api/v1/audits/{id}.
Errors & limits
Errors share one shape:
{ "error": { "code": "insufficient_credits", "message": "…", "details": { "required": 3, "balance": 1 } } } | Status | Code | Meaning |
|---|---|---|
400 | validation_error | Malformed JSON or invalid fields; `details.issues` lists each problem. |
401 | unauthorized | Missing, malformed or revoked key, or the account has no Agency plan. |
402 | insufficient_credits | Not enough credits for the product; `details.required` and `details.balance` are included. |
404 | not_found | The audit does not exist or belongs to another account. |
415 | unsupported_media_type | POST bodies must be sent with `Content-Type: application/json`. |
429 | rate_limited | More than 10 create requests per minute per key. Back off and retry. |
Creating audits is limited to 10 requests per minute per key; reads are not rate-limited beyond fair use. Request bodies are capped at 64 KB. Up to 10 active keys per account. Questions? Email hello@webvitalist.com.