Skip to content
Auto

Webhooks

Pass webhookUrl on any video-jobs submit call and Megomi POSTs the terminal result there as soon as the job resolves — the same result shape that call would have returned inline. This is the recommended way to collect a result after a 202; see Getting Your Result for the alternative (resubmitting).

  • Up to 3 attempts, with a short linear backoff between them.
  • A delivery that exhausts its attempts is not retried further — resubmit the original request to get the result inline instead (see Getting Your Result), at no extra charge.
  • Every delivery is Content-Type: application/json, POST, with the same body shape shown below.
Field Type Description
requestId string The opaque req_… ID from the original submit response
itemId string Resolved TikTok item ID
status string success | not-found | blocked | timed-out | cancelled
requestCount number How many requests coalesced onto this job
result object | null Same shape as the endpoint you submitted through
finishReason string | null Present when the job didn’t resolve to success

Every delivery carries these headers:

Header Value
X-TTScraper-Request-Id The requestId this delivery is for
X-TTScraper-Timestamp Unix seconds when this attempt was sent
X-TTScraper-Signature v1=<hex HMAC-SHA256(webhook secret, timestamp + "." + raw body)>

To verify a delivery: recompute the HMAC over the raw request body (before any JSON parsing), compare it to the signature in constant time, and reject anything whose timestamp is more than ~5 minutes from your own clock.

The signature covers timestamp + "." + rawBody — not the parsed JSON, so re-serializing the body before verifying will produce a mismatch if your JSON library reorders or reformats keys.

A timestamp check matters as much as the signature itself: without it, a captured delivery (and its valid signature) could be replayed indefinitely.

const crypto = require('node:crypto');
function verify(secret, rawBody, timestamp, signatureHeader) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = 'v1=' + crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return signatureHeader.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}
{
"requestId": "req_9f1kQ3zP7mLxA2dR",
"itemId": "7123456789012345678",
"status": "success",
"requestCount": 2,
"result": {
"likes": 128400,
"views": 2114500,
"comments": 3021,
"shares": 884,
"saves": 2110,
"hasBasket": true
},
"finishReason": null
}