Developer API

The EngageCheck API

Vet a whole roster of creators before a dollar leaves the budget. The same five-signal analysis behind our dashboard, exposed as clean JSON: audit one handle or fire fifty at once, get an audience quality score from 0 to 100, fake-follower percentage, bot and pod flags, and the real engagement rate. Submit an audit and get a report ID back instantly. Poll for the result or receive it via webhook. Built for agencies running creator due diligence at scale.

Get API accessAPI pricing
Base URL
https://engagecheck.com/api/v1
Auth
Bearer ec_live_ key
Format
JSON request and response
On this page
Pricing

Public per-report pricing

Every plan and every pack includes full API access. A credit is a credit, whether you spend it in the dashboard or over the API, and credits never expire. There is no separate API tier, no sales call, and no annual commitment: create an account, buy credits, generate a key, and start auditing.

Monthly plans

Credits arrive with each monthly invoice and roll over forever. Cancel anytime in one click via the Stripe portal and keep every unused credit.

Pro$49/mo · 35 credits/mo$1.40 / report
Agency$199/mo · 160 credits/mo$1.24 / report
Scale$499/mo · 500 credits/mo$1.00 / report

$1.40 per report on Pro down to $1.00 per report on Scale.

One-off credit packs

No subscription required. Buy a pack when you need it; credits never expire and the API draws from the same balance.

Single Audit$5 · 1 credit$5.00 / report
Light$10 · 3 credits$3.33 / report
Growth$20 · 8 credits$2.50 / report
Agency 50$99 · 50 credits$1.98 / report
Agency 200$299 · 200 credits$1.50 / report
Agency 500$599 · 500 credits$1.20 / report

$5.00 for a single report down to $1.20 per report on Agency 500.

  • Batch endpoint: up to 50 handles per call, with per-handle webhooks.
  • Failed, private, or not-found audits are refunded automatically - you only pay for results.
  • Rate limit: 60 requests per minute per key by default. Need more? Contact us.
  • Self-serve keys: create and revoke them at /account/api - no approval queue.
APIHow you get accessPricing
EngageCheckSelf-serve keys, no sales callMonthly plans from $49/mo, or one-off packs. $1.00 to $5.00 per report, all pricing public.
Modash Discovery APIAnnual contract; no monthly or pay-as-you-go plansStarts at $16,200 per year, per their public pricing page. Source: modash.io API pricing
HypeAuditor APIVia sales: request a demo to discuss accessNo public API pricing listed on their site. Source: hypeauditor.com API page

Competitor details reflect their public pages as of July 2026; follow the source links for their current terms.

Authentication

Authenticate with a Bearer key

Every request must carry your API key. Send it in the Authorization header as a Bearer token, or in an X-API-Key header if your client cannot set Authorization. Keys look like ec_live_... and draw from your account's credit balance with a per-minute rate limit. Keys are self-serve: sign in and create or revoke them at /account/api - every account can, no approval queue. Treat your key like a password and never ship it in client-side code.

Authorization header
Authorization: Bearer ec_live_8Fq2Zx...
Or, the X-API-Key header
X-API-Key: ec_live_8Fq2Zx...
POST/audits

Run a single audit

Submit one Instagram handle for analysis. The call returns 202 Accepted immediately with a report id and a poll_url, then the scrape, analysis, and scoring run in the background, usually 30 to 90 seconds. Poll GET /api/v1/audits/{id} until status is complete or error, or pass a webhook_url to have the finished report delivered to you. This consumes one credit. If the scrape fails or the account is private, the credit is automatically refunded and the report comes back with an error status. If we already hold a fresh report for this handle, the response returns instantly with status complete and the full report inline.

FieldTypeRequiredDescription
usernamestringYesInstagram handle to audit. The @, full URLs, and casing are all normalized.
webhook_urlstringNoHTTPS endpoint to receive the result. Fires audit.completed on success, audit.failed on failure.
Example request
cURL
# 1. Submit the audit. Returns 202 Accepted right away with a report id.
curl -X POST https://engagecheck.com/api/v1/audits \
  -H "Authorization: Bearer ec_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"username": "natgeo"}'

# 2. Poll the report id until status is complete or error.
curl https://engagecheck.com/api/v1/audits/142 \
  -H "Authorization: Bearer ec_live_xxx"
Node (fetch)
// 1. Submit the audit. Returns 202 immediately with a report id.
const submit = await fetch("https://engagecheck.com/api/v1/audits", {
  method: "POST",
  headers: {
    "Authorization": "Bearer ec_live_xxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ username: "natgeo" }),
});
const { id, poll_url } = await submit.json();

// 2. Poll until the audit is complete or errors out (usually 30 to 90s).
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
let report;
while (true) {
  const res = await fetch("https://engagecheck.com" + poll_url, {
    headers: { "Authorization": "Bearer ec_live_xxx" },
  });
  report = await res.json();
  if (report.status === "complete" || report.status === "error") break;
  await sleep(3000);
}

if (report.status === "complete") {
  console.log(report.aqs, report.verdict);
} else {
  console.error(report.error_message);
}
Python (requests)
import time
import requests

# 1. Submit the audit. Returns 202 immediately with a report id.
submit = requests.post(
    "https://engagecheck.com/api/v1/audits",
    headers={"Authorization": "Bearer ec_live_xxx"},
    json={"username": "natgeo"},
)
poll_url = submit.json()["poll_url"]

# 2. Poll until the audit is complete or errors out (usually 30 to 90s).
while True:
    res = requests.get(
        "https://engagecheck.com" + poll_url,
        headers={"Authorization": "Bearer ec_live_xxx"},
    )
    report = res.json()
    if report["status"] in ("complete", "error"):
        break
    time.sleep(3)

if report["status"] == "complete":
    print(report["aqs"], report["verdict"])
else:
    print(report["error_message"])
Example response, 202 Accepted
Queued, poll for the result
{
  "id": 142,
  "username": "natgeo",
  "status": "pending",
  "credits_remaining": 249,
  "poll_url": "/api/v1/audits/142",
  "webhook_url": null
}
Cache hit, returned complete instantly
{
  "id": 142,
  "username": "natgeo",
  "status": "complete",
  "aqs": 88,
  "verdict": "green",
  "report": {
    "username": "natgeo",
    "aqs": 88,
    "verdict": "green",
    "summary": "@natgeo scored 88 out of 100, indicating a largely authentic audience...",
    "signals": [
      {
        "key": "engagement_rate",
        "label": "Engagement Rate",
        "score": 92,
        "weightPct": 25,
        "verdict": "green",
        "value": "3.10% ER",
        "detail": "Engagement sits inside the healthy 1% to 10% band."
      }
    ],
    "commentQuality": {
      "bot_comment_percentage": 4,
      "pod_activity_detected": false,
      "linguistic_summary": "Comments read as genuine conversation."
    },
    "profile": {
      "fullName": "National Geographic",
      "followersCount": 280000000,
      "followingCount": 142,
      "postsCount": 29000,
      "isPrivate": false,
      "isVerified": true
    },
    "generatedAt": "2026-06-25T17:00:00.000Z"
  },
  "credits_remaining": 249,
  "poll_url": "/api/v1/audits/142",
  "webhook_url": null
}
Polling the report

Send a GET to the poll_url until status reaches complete or error. While the audit runs it reports an interim status like scraping or analyzing.

cURL
curl https://engagecheck.com/api/v1/audits/142 \
  -H "Authorization: Bearer ec_live_xxx"
In-progress response, 200 OK
{
  "id": 142,
  "username": "natgeo",
  "status": "analyzing",
  "error_message": null,
  "created_at": "2026-06-25 17:00:00",
  "completed_at": null
}
POST/audits/batch

Batch audits

The agency workflow: audit up to 50 handles in one call. Credits for the whole batch are reserved up front, so the request returns 402 if the key cannot cover it. The call returns 202 Accepted immediately with an array of report ids and poll_urls, then the audits process sequentially in the background. Poll each id, or pass a webhook_url to receive one webhook per handle as each one completes. Any audit that fails comes back with an error status and is refunded one credit, so you only pay for results.

FieldTypeRequiredDescription
usernamesstring[]YesArray of Instagram handles, maximum 50. Duplicates and blanks are dropped.
webhook_urlstringNoHTTPS endpoint to receive results. Fires one webhook per handle: audit.completed or audit.failed.
Example request
cURL
# Submit up to 50 handles. Returns 202 with a report id per handle.
curl -X POST https://engagecheck.com/api/v1/audits/batch \
  -H "Authorization: Bearer ec_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"usernames": ["natgeo", "nasa"]}'

# Then poll each returned id until it is complete or error.
curl https://engagecheck.com/api/v1/audits/143 \
  -H "Authorization: Bearer ec_live_xxx"
Node (fetch)
// Submit the batch. Returns 202 immediately with one id per handle.
const submit = await fetch("https://engagecheck.com/api/v1/audits/batch", {
  method: "POST",
  headers: {
    "Authorization": "Bearer ec_live_xxx",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ usernames: ["natgeo", "nasa"] }),
});
const { results } = await submit.json();

// Each row carries its own poll_url. Poll them until every audit settles.
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function poll(pollUrl) {
  while (true) {
    const res = await fetch("https://engagecheck.com" + pollUrl, {
      headers: { "Authorization": "Bearer ec_live_xxx" },
    });
    const report = await res.json();
    if (report.status === "complete" || report.status === "error") return report;
    await sleep(3000);
  }
}
const reports = await Promise.all(results.map((r) => poll(r.poll_url)));
for (const r of reports) console.log(r.username, r.aqs ?? r.error_message);
Python (requests)
import time
import requests

submit = requests.post(
    "https://engagecheck.com/api/v1/audits/batch",
    headers={"Authorization": "Bearer ec_live_xxx"},
    json={"usernames": ["natgeo", "nasa"]},
)
results = submit.json()["results"]

def poll(poll_url):
    while True:
        res = requests.get(
            "https://engagecheck.com" + poll_url,
            headers={"Authorization": "Bearer ec_live_xxx"},
        )
        report = res.json()
        if report["status"] in ("complete", "error"):
            return report
        time.sleep(3)

for row in results:
    report = poll(row["poll_url"])
    print(report["username"], report.get("aqs", report.get("error_message")))
Example response, 202 Accepted
{
  "results": [
    { "id": 143, "username": "natgeo", "status": "pending", "poll_url": "/api/v1/audits/143" },
    { "id": 144, "username": "nasa", "status": "pending", "poll_url": "/api/v1/audits/144" }
  ],
  "credits_remaining": 247,
  "webhook_url": null
}
GET/audits

List recent reports

Page through the most recent reports as a lightweight summary array. Read-only, so it never consumes a credit.

FieldTypeRequiredDescription
limitintegerNoRows to return, 1 to 100. Defaults to 25.
offsetintegerNoRows to skip for pagination. Defaults to 0.
Example request
cURL
curl "https://engagecheck.com/api/v1/audits?limit=25&offset=0" \
  -H "Authorization: Bearer ec_live_xxx"
Example response, 200 OK
{
  "data": [
    {
      "id": 144,
      "username": "nasa",
      "status": "complete",
      "aqs": 91,
      "verdict": "green",
      "created_at": "2026-06-25 17:01:00",
      "completed_at": "2026-06-25 17:01:38"
    }
  ],
  "count": 1,
  "limit": 25,
  "offset": 0
}
GET/audits/{id}

Get a report by id

Fetch one report by its numeric id. This is the endpoint you poll after submitting an audit. Status moves through pending, scraping, analyzing, and then complete or error. While the audit runs the body carries just the status; once status is complete the full report with the complete signal breakdown is included, and on failure error_message explains why. Returns 404 if no report matches. Read-only, no credit cost.

FieldTypeRequiredDescription
idintegerYesThe report id returned by an audit call. Passed in the path.
Example request
cURL
curl https://engagecheck.com/api/v1/audits/142 \
  -H "Authorization: Bearer ec_live_xxx"
Example response, complete
{
  "id": 142,
  "username": "natgeo",
  "status": "complete",
  "aqs": 88,
  "verdict": "green",
  "report": {
    "username": "natgeo",
    "aqs": 88,
    "verdict": "green",
    "summary": "@natgeo scored 88 out of 100, indicating a largely authentic audience...",
    "signals": [
      {
        "key": "engagement_rate",
        "label": "Engagement Rate",
        "score": 92,
        "weightPct": 25,
        "verdict": "green",
        "value": "3.10% ER",
        "detail": "Engagement sits inside the healthy 1% to 10% band."
      }
    ],
    "commentQuality": {
      "bot_comment_percentage": 4,
      "pod_activity_detected": false,
      "linguistic_summary": "Comments read as genuine conversation."
    },
    "profile": {
      "fullName": "National Geographic",
      "followersCount": 280000000,
      "followingCount": 142,
      "postsCount": 29000,
      "isPrivate": false,
      "isVerified": true
    },
    "generatedAt": "2026-06-25T17:00:00.000Z"
  },
  "error_message": null,
  "created_at": "2026-06-25 17:00:00",
  "completed_at": "2026-06-25 17:00:42"
}
Webhooks

Get the report pushed to you

Pass an optional webhook_url on POST /audits or POST /audits/batch and we POST the result to that URL the moment each audit settles, so you never have to poll. A batch fires one webhook per handle as it finishes. Delivery is best-effort: a webhook that fails or times out never affects the audit or your credits. Only https URLs are accepted, with http allowed for local testing.

On success we send an audit.completed event carrying the full report. On failure we send an audit.failed event with the error message.

audit.completed payload
{
  "event": "audit.completed",
  "id": 142,
  "username": "natgeo",
  "aqs": 88,
  "verdict": "green",
  "report": {
    "username": "natgeo",
    "aqs": 88,
    "verdict": "green",
    "summary": "@natgeo scored 88 out of 100, indicating a largely authentic audience...",
    "signals": [
      {
        "key": "engagement_rate",
        "label": "Engagement Rate",
        "score": 92,
        "weightPct": 25,
        "verdict": "green",
        "value": "3.10% ER",
        "detail": "Engagement sits inside the healthy 1% to 10% band."
      }
    ],
    "commentQuality": {
      "bot_comment_percentage": 4,
      "pod_activity_detected": false,
      "linguistic_summary": "Comments read as genuine conversation."
    },
    "profile": {
      "fullName": "National Geographic",
      "followersCount": 280000000,
      "followingCount": 142,
      "postsCount": 29000,
      "isPrivate": false,
      "isVerified": true
    },
    "generatedAt": "2026-06-25T17:00:00.000Z"
  },
  "credits_remaining": 249
}
audit.failed payload
{
  "event": "audit.failed",
  "id": 142,
  "username": "thisuserdoesnotexist",
  "error": "Account was not found or has been deleted."
}
GET/credits

Check your balance

Return the credits left on the key, its per-minute rate limit, and the key prefix so you can confirm which key is in use without exposing the secret.

Example request
cURL
curl https://engagecheck.com/api/v1/credits \
  -H "Authorization: Bearer ec_live_xxx"
Example response, 200 OK
{
  "credits_remaining": 247,
  "rate_limit_per_min": 60,
  "key_prefix": "ec_live_8Fq2Zx"
}
Rate limits

Per-key rate limiting

Each key has a sliding one-minute window, defaulting to 60 requests per minute. Every response carries the current state in its headers, so you can pace your worker without guessing. When you exceed the window the API returns 429 with a Retry-After header that tells you how many seconds to wait.

Response headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
X-RateLimit-Reset: 1782493260
Retry-After: 12   (sent only on a 429)
Errors

Error codes

Errors always come back as JSON in the shape { "error": "message" } with the matching HTTP status code.

StatusNameWhen it happens
202AcceptedThe audit was queued. POST returns immediately with a report id and poll_url; the result arrives by polling or webhook.
400Bad RequestMissing or malformed input, like an empty username or a batch over 50 handles.
401UnauthorizedThe API key is missing, invalid, or revoked.
402Payment RequiredThe key has no credits left to cover the request.
404Not FoundNo report exists for the requested id.
429Too Many RequestsThe per-key rate limit was exceeded. Check the Retry-After header.
502Bad GatewayOnly seen on GET /audits/{id} as a transient gateway issue. POST no longer returns 502: a failed scrape, or a private or deleted account, surfaces as the report's error status or an audit.failed webhook.
API terms

The rules, in plain English

Using the API means agreeing to these rules on top of our Terms of Service. They exist to keep the API fast and fair for everyone.

Keys are per-account

Each key draws from your account's credit balance and you are responsible for every request made with it. Share keys inside your team, not outside your organization, and never ship one in client-side code.

Use reports, don't resell the raw data

Build the scores and reports into your own workflow, tools, and client deliverables. Don't resell or republish raw audit data as a standalone dataset, feed, or competing audit product.

No scraping through us

Don't use the API as a proxy to harvest Instagram profile data at scale or to assemble third-party datasets for redistribution. The product is the audit, not a scraping layer.

Fair-use rate limits

Every key is rate limited (60 requests per minute by default). If you need more headroom, contact us and we'll raise it - don't work around the limiter by rotating keys.

Abuse can suspend a key

We may suspend keys that break these rules. Where possible we email you first, and unused credits stay on your account.

Ready to audit at scale?

Every account gets API access. Create a free account, generate a key in your dashboard, and start auditing in minutes. Buy credits whenever you need more.