Hookroast
Sign in

API Reference

Integrate HookRoast's reputation engine into your own apps, and try every endpoint live.

The HookRoast API is REST over HTTPS. All requests and responses are JSON. The base URL is:

base url
https://api.hookroast.com/api/v1

Create an API key on the Developers page (sign-in required). Every call draws from your monthly credit wallet.

Live console

Paste an API key to run the Try it panels against the real API. Calls spend credits. Your key is stored only in this browser. Don't use a production key on a shared machine.

No key set. Try it panels will prompt you.

Authentication

Pass your API key in the x-api-key header, or as a bearer token. Keys look like hr_live_… and are shown only once at creation.

curl
curl https://api.hookroast.com/api/v1/reputation \
  -X POST \
  -H "x-api-key: hr_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"domain":"acme.io"}'

Or Authorization: Bearer hr_live_your_key_here. A missing or revoked key returns 401.

Rate limits

Requests are rate limited per API key (default 60 requests / 60s). Every response includes:

response headers
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
x-ratelimit-reset: 1782684309

Exceeding the limit returns 429 TOO_MANY_REQUESTS with a retry-after header (seconds).

Credits

Each call spends credits from your monthly wallet: a reputation scan costs 5 credits, a bulk scan 5 × number of targets, and email validation 1 credit per email. When the wallet is exhausted the API returns 403 FORBIDDEN. Paid plans can buy top-up credits from the Billing page.

Errors

Errors use standard HTTP status codes and a consistent JSON body:

error shape
{ "error": "FORBIDDEN", "message": "…human readable…" }
  • 400 / 422 VALIDATION_ERROR: bad request body
  • 401 UNAUTHORIZED: missing/invalid API key
  • 403 INSUFFICIENT_CREDITS: the wallet can't cover the call; details has required, remaining and canTopUp
  • 403 PLAN_UPGRADE_REQUIRED: the feature needs a higher plan; details.requiredTier names it
  • 403 FORBIDDEN: not allowed for this key or role
  • 404 NOT_FOUND: unknown id
  • 429 TOO_MANY_REQUESTS: rate limited

Email roast

Score a cold email for spam risk and copy strength and get a rewrite. Optionally include the subject line and up to three follow-ups. Costs 3 credits, plus 2 per follow-up. Browser-extension keys can call this endpoint on any plan.

POST/api/v1/analysis

Runs synchronously and usually returns in 5 to 15 seconds.

Body parameters

rawTextstringrequired

The email body, 20 to 8,000 characters. Merge tags are kept as-is.

targetPersonastringrequired

Who the email is for.

subjectLinestringoptional

Optional subject line to score.

followUpsstring[]optional

Up to 3 follow-up emails, in order.

Response fields

spamScoreinteger

1 to 100, higher is safer.

riskLevelstring

Low · Medium · High.

flaggedPhrasesstring[]

Exact phrases that hurt deliverability.

critiquestring

What's working and what isn't.

optimizedRewritestring

A rewritten version.

details.subScoresobject

hook, personalization, valueProp, cta, brevity, tone (0 to 100).

details.followUpsarray

Per-step score, critique and rewrite.

example response
{
  "id": "4f1a…", "spamScore": 82, "riskLevel": "Low", "flaggedPhrases": [],
  "critique": "Specific hook, but the ask is vague.",
  "optimizedRewrite": "Hi {{firstName|there}}, …",
  "details": { "subScores": { "hook": 78, "personalization": 64, "valueProp": 55, "cta": 48, "brevity": 90, "tone": 81 } }
}
request
curl https://api.hookroast.com/api/v1/analysis \
  -X POST \
  -H "x-api-key: hr_live_…" \
  -H "Content-Type: application/json" \
  -d '{"rawText":"Hi {{firstName|there}}, saw Acme is hiring SDRs. Worth a quick chat?","targetPersona":"Heads of sales at Series A SaaS","subjectLine":"Quick question"}'
POSThttps://api.hookroast.com/api/v1/analysis
Headersx-api-key: not setcontent-type: application/json
Show as cURL
curl https://api.hookroast.com/api/v1/analysis \
  -X POST \
  -H "x-api-key: hr_live_your_key"
 \
  -H "Content-Type: application/json" \
  -d '{
  "rawText": "Hi Sam, saw Acme is hiring SDRs. Worth a quick chat next week?",
  "targetPersona": "Heads of sales at Series A SaaS"
}'

Reputation scan

Start a scan for a single email address or domain. Scans run asynchronously: create one, then poll the GET endpoint until status is COMPLETED.

POST/api/v1/reputation

Create a scan. The body must contain exactly one of the fields below. Returns the scan id to poll.

Body parameters

emailstringoptional

Email address to scan. Provide this or `domain` (exactly one).

domainstringoptional

Bare domain to scan, e.g. acme.io. Provide this or `email` (exactly one).

Response fields

idstring

Unique scan id. Use it to poll the GET endpoint.

statusstring

PENDING · PROCESSING · COMPLETED · FAILED.

emailstring | null

The email scanned, or null for a domain scan.

domainstring

The domain scanned.

example response
{ "id": "b5f7c8ac-…", "status": "PENDING", "email": null, "domain": "acme.io" }
request
curl https://api.hookroast.com/api/v1/reputation \
  -X POST \
  -H "x-api-key: hr_live_…" \
  -H "Content-Type: application/json" \
  -d '{"domain":"acme.io"}'
POSThttps://api.hookroast.com/api/v1/reputation
Headersx-api-key: not setcontent-type: application/json
Show as cURL
curl https://api.hookroast.com/api/v1/reputation \
  -X POST \
  -H "x-api-key: hr_live_your_key"
 \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "acme.io"
}'

GET/api/v1/reputation/:id

Fetch a scan. Poll until status is COMPLETED. The report includes the overall score, per-category scores, verified analyzer data, and AI analysis.

Path parameters

idstringrequired

The scan id returned by POST /reputation.

Response fields

idstring

The scan id.

statusstring

Poll until COMPLETED (or FAILED).

overallScoreinteger | null

Overall reputation 0-100; null until completed.

gradestring | null

Excellent · Good · Fair · Poor.

reportobject | null

Full report; null until completed.

report.confidenceinteger

0-100; share of analyzers that succeeded.

report.categoryScoresarray

[{ category, score }] for each scored category.

report.analysisobject

Verified per-analyzer data, keyed by analyzer.

report.aiobject

AI insights: trustScore, riskLevel, replyProbability, summary, recommendations[].

example response
{
  "id": "b5f7c8ac-…",
  "status": "COMPLETED",
  "overallScore": 84,
  "grade": "Good",
  "report": {
    "confidence": 100,
    "categoryScores": [ { "category": "Deliverability", "score": 88 } ],
    "ai": {
      "trustScore": 84, "riskLevel": "Low", "replyProbability": 70, "summary": "…",
      "recommendations": [ { "problem": "…", "impact": "Medium", "recommendation": "…", "expectedImprovement": "+5 points" } ]
    }
  }
}
request
curl https://api.hookroast.com/api/v1/reputation/SCAN_ID \
  -X GET \
  -H "x-api-key: hr_live_…"
GEThttps://api.hookroast.com/api/v1/reputation/:id
Headersx-api-key: not set
Show as cURL
curl https://api.hookroast.com/api/v1/reputation/:id \
  -X GET \
  -H "x-api-key: hr_live_your_key"

Bulk reputation

Score many emails/domains in one job (up to 1,000 targets). Targets can mix emails and bare domains.

POST/api/v1/bulk-reputation

Submit a batch of targets. Returns a job id to poll.

Body parameters

targetsstring[]required

Array of emails and/or bare domains to score. 1-1,000 entries.

Response fields

idstring

Job id. Use it to poll the GET endpoint.

statusstring

PENDING · PROCESSING · COMPLETED · FAILED.

totalCountinteger

Number of targets queued.

example response
{ "id": "4102e918-…", "status": "PENDING", "totalCount": 2 }
request
curl https://api.hookroast.com/api/v1/bulk-reputation \
  -X POST \
  -H "x-api-key: hr_live_…" \
  -H "Content-Type: application/json" \
  -d '{"targets":["stripe.com","example.org"]}'
POSThttps://api.hookroast.com/api/v1/bulk-reputation
Headersx-api-key: not setcontent-type: application/json
Show as cURL
curl https://api.hookroast.com/api/v1/bulk-reputation \
  -X POST \
  -H "x-api-key: hr_live_your_key"
 \
  -H "Content-Type: application/json" \
  -d '{
  "targets": ["stripe.com", "example.org"]
}'

GET/api/v1/bulk-reputation/:id

Fetch the job with per-target results once processing completes.

Path parameters

idstringrequired

The job id returned by POST /bulk-reputation.

Response fields

idstring

The job id.

statusstring

Poll until COMPLETED (or FAILED).

totalCountinteger

Total targets in the job.

completedCountinteger

Targets scored so far.

itemsarray

Per-target results (see fields below).

items[].targetstring

The email or domain scored.

items[].statusstring

COMPLETED or FAILED for that target.

items[].overallScoreinteger | null

Overall reputation 0-100.

items[].gradestring | null

Excellent · Good · Fair · Poor.

items[].riskLevelstring | null

Low · Medium · High · Critical.

items[].reportobject | null

Full report for that target.

example response
{
  "id": "4102e918-…", "status": "COMPLETED", "totalCount": 2, "completedCount": 2,
  "items": [ { "target": "stripe.com", "status": "COMPLETED", "overallScore": 86, "grade": "Good", "riskLevel": "Low" } ]
}
request
curl https://api.hookroast.com/api/v1/bulk-reputation/JOB_ID \
  -X GET \
  -H "x-api-key: hr_live_…"
GEThttps://api.hookroast.com/api/v1/bulk-reputation/:id
Headersx-api-key: not set
Show as cURL
curl https://api.hookroast.com/api/v1/bulk-reputation/:id \
  -X GET \
  -H "x-api-key: hr_live_your_key"

Real-time verify

Verify a single email address synchronously: one request, one answer. Built for signup forms and lead capture, so you can block disposable and undeliverable addresses before they enter your product. Costs 1 credit per call.

POST/api/v1/verify

Also available as GET /api/v1/verify?email=jane@acme.io for easy wiring into form validators. Responses typically return in 1-3 seconds (the SMTP probe dominates); set a client timeout of at least 10 seconds.

Body parameters

emailstringrequired

The email address to verify.

Response fields

emailstring

The normalized email address.

statusstring

VALID · INVALID · RISKY (deliverable but may bounce: catch-all domain or role mailbox) · UNKNOWN (the mail server would not confirm).

reasonstring | null

invalid_format · disposable_domain · domain_not_found · mailbox_not_found · catch_all · role_account · unverifiable (null when valid). Catch-all and role detection need Growth or above.

example response
{ "email": "jane@acme.io", "status": "VALID", "reason": null }
request
curl https://api.hookroast.com/api/v1/verify \
  -X POST \
  -H "x-api-key: hr_live_…" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@acme.io"}'
POSThttps://api.hookroast.com/api/v1/verify
Headersx-api-key: not setcontent-type: application/json
Show as cURL
curl https://api.hookroast.com/api/v1/verify \
  -X POST \
  -H "x-api-key: hr_live_your_key"
 \
  -H "Content-Type: application/json" \
  -d '{
  "email": "jane@acme.io"
}'

Email validation

Validate a list of email addresses for format, domain deliverability (MX), disposable providers, and, where reachable, mailbox existence over SMTP. Runs asynchronously; poll the GET endpoint for results. Costs 1 credit per email.

POST/api/v1/email-validation

Submit a batch of emails. Returns a job id to poll.

Body parameters

emailsstring[]required

Email addresses to validate. 1-5,000 entries.

Response fields

idstring

Job id. Use it to poll the GET endpoint.

statusstring

PENDING · PROCESSING · COMPLETED · FAILED.

totalCountinteger

Number of emails submitted.

example response
{ "id": "9c1e…", "status": "PENDING", "totalCount": 2 }
request
curl https://api.hookroast.com/api/v1/email-validation \
  -X POST \
  -H "x-api-key: hr_live_…" \
  -H "Content-Type: application/json" \
  -d '{"emails":["jane@acme.io","ghost@acme.io"]}'
POSThttps://api.hookroast.com/api/v1/email-validation
Headersx-api-key: not setcontent-type: application/json
Show as cURL
curl https://api.hookroast.com/api/v1/email-validation \
  -X POST \
  -H "x-api-key: hr_live_your_key"
 \
  -H "Content-Type: application/json" \
  -d '{
  "emails": ["jane@acme.io", "ghost@acme.io"]
}'

GET/api/v1/email-validation/:id

Fetch the job with per-email results once validation completes.

Path parameters

idstringrequired

The job id returned by POST /email-validation.

Response fields

idstring

The job id.

statusstring

Poll until COMPLETED (or FAILED).

totalCountinteger

Total emails in the job.

validCountinteger

Emails found valid.

invalidCountinteger

Emails that will bounce.

riskyCountinteger

Deliverable but risky (catch-all or role mailbox).

unknownCountinteger

Mail server wouldn't confirm the mailbox.

resultsarray

Per-email outcomes (see fields below).

results[].emailstring

The normalized email address.

results[].statusstring

VALID · INVALID · RISKY (deliverable but may bounce: catch-all domain or role mailbox) · UNKNOWN (the mail server would not confirm).

results[].reasonstring | null

invalid_format · disposable_domain · domain_not_found · mailbox_not_found · catch_all · role_account · unverifiable (null when valid). Catch-all and role detection need Growth or above.

results[].flagsstring[]

Extra attributes such as free_provider. Omitted when empty.

example response
{
  "id": "9c1e…", "status": "COMPLETED", "totalCount": 2, "validCount": 1, "invalidCount": 1, "riskyCount": 0, "unknownCount": 0,
  "results": [
    { "email": "jane@acme.io", "status": "VALID", "reason": null },
    { "email": "ghost@acme.io", "status": "INVALID", "reason": "mailbox_not_found" }
  ]
}
request
curl https://api.hookroast.com/api/v1/email-validation/JOB_ID \
  -X GET \
  -H "x-api-key: hr_live_…"
GEThttps://api.hookroast.com/api/v1/email-validation/:id
Headersx-api-key: not set
Show as cURL
curl https://api.hookroast.com/api/v1/email-validation/:id \
  -X GET \
  -H "x-api-key: hr_live_your_key"

Webhooks

Register webhook URLs on the Developers page to be notified instead of polling (Pro and above). Events: reputation.completed, bulk_reputation.completed, email_validation.completed, monitor.alert, test_email.completed, dmarc.report_received, widget.lead. We POST:

POST body to your URL
{
  "event": "reputation.completed",
  "data": { "scanId": "b5f7c8ac-…" },
  "timestamp": "2026-06-29T12:00:00.000Z"
}

Each delivery is signed: x-hookroast-signature is the hex HMAC-SHA256 of the raw request body using the signing secret shown when you created the webhook. Verify it before trusting the payload. Requests also carry x-hookroast-event and a unique x-hookroast-delivery id you can use to ignore duplicates. Any non-2xx response (or no response within 5 seconds) is retried after 1, 5, 30, 120 and 360 minutes.

node verification
import { createHmac, timingSafeEqual } from "crypto";

function verify(rawBody, signature, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return expected.length === signature.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}