Quickstart in 60 seconds
- Get a beta API key from /beta or ask an admin to create one.
- Send a public Threads post URL to
POST /v1/threads/video. - Choose video
download_mode, callPOST /v1/threads/commentsfor replies, or usePOST /v1/threads/search.
curl -X POST https://api.clipmagic.ru/v1/threads/video \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"url":"https://www.threads.net/@example/post/example","download_mode":"proxy","include_metadata":true,"include_thumbnail":true}'
Endpoint: Any Threads post
POST /v1/threads/post accepts the same URL and mode options as video, and succeeds for text-only posts, images, videos and mixed carousels.
{"url":"https://www.threads.com/@example/post/ABC123","download_mode":"proxy"}
media[] contains attachments in publication order. Each has id, position, type, thumbnail_url and its own variants[]. Choose the is_default variant within each attachment to download one file per carousel item. Posters are not separate attachments.
Each proxy variant returns download_url and expires_at; its direct URL is hidden. Downloads use the same current-owner checks, byte limits and GET/HEAD endpoint. Images support JPEG, PNG, WebP, GIF, AVIF and HEIC/HEIF; extensionless source URLs are checked by actual MIME type. SVG and unknown MIME are rejected.
retrieval.status. A partial response includes a reason and can contain empty variant lists. Complete means the selected source exposed its known attachment list, not parity with every logged-in audience. No arbitrary page previews, recommendations or quoted media are substituted.Text-only posts return media: [] in every mode. metadata_only hides variant URLs; set include_thumbnail=false to hide posters too. Invalid or unknown parameters return VALIDATION_ERROR. Source cache is capped at 300 seconds and tokens are issued separately for each caller; a source URL may expire before the token.
Endpoint: Search Threads
POST /v1/threads/search returns a normalized bounded public window. It supports ranked posts, recent posts, and profiles.
| Field | Type | Required | Description |
|---|---|---|---|
| query | string 1–256 | yes | Search phrase; whitespace is normalized. |
| search_type | top | recent | profiles | no | Result family; default top. |
| limit | integer 1–50 | no | Maximum returned records; default 20. |
curl -X POST https://api.clipmagic.ru/v1/threads/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query":"OpenAI","search_type":"profiles","limit":10}'
window.limit and window.returned.Endpoint: Export Threads Comments
POST /v1/threads/comments returns a flat oldest-first page containing top-level and nested public replies. Use parent_id and root_post_id to reconstruct the tree.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Public Threads post URL. |
| limit | integer 1–100 | no | Page size; default 50. |
| cursor | string | null | no | Opaque signed cursor from the preceding page, for the same URL and limit. |
let cursor = null;
do {
const response = await fetch("https://api.clipmagic.ru/v1/threads/comments", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": process.env.THREADS_API_KEY },
body: JSON.stringify({ url: "https://www.threads.com/@example/post/example", limit: 50, cursor })
});
const page = await response.json();
if (!response.ok) throw new Error(page.error.message);
console.log(page.comments);
cursor = page.retrieval.status === "paginated" ? page.pagination.next_cursor : null;
if (page.retrieval.status === "partial") console.warn("Public provider returned a partial conversation");
} while (cursor);
A traversal stays pinned to its first provider. Public Relay uses the current refetch cursor when available; the logged-out browser capability can continue through a deterministic public window; configured authorized providers forward only their own upstream cursor.
partial means the selected public provider cannot expose more replies. It is not a complete snapshot guarantee.Authentication
Use the X-API-Key header on resolve requests. Full API keys are shown once at creation and are stored server-side only as HMAC hashes.
Endpoint: Resolve Threads Video
POST /v1/threads/video accepts a Threads post URL and returns post metadata plus video media variants when available.
| Field | Type | Required | Description |
|---|---|---|---|
| url | string | yes | Public Threads post URL. |
| download_mode | url | metadata_only | proxy | no | Controls whether direct URLs, metadata only, or temporary proxy links are returned. |
| include_metadata | boolean | no | Include post metadata fields. |
| include_thumbnail | boolean | no | Include thumbnail URLs when available. |
download_mode comparison
url
Returns validated media URLs when source allows it. Best for server-side workflows that can handle expiring signed URLs.
metadata_only
Returns post and media metadata without download URLs. Best for testing, indexing and previews.
proxy
Returns temporary /v1/download/:token links. The API streams media and does not store videos permanently.
Code examples
JavaScript
const response = await fetch("https://api.clipmagic.ru/v1/threads/video", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": process.env.THREADS_API_KEY },
body: JSON.stringify({ url: "https://www.threads.net/@example/post/example", download_mode: "metadata_only" })
});
const data = await response.json();
Python
import os, requests
response = requests.post(
"https://api.clipmagic.ru/v1/threads/video",
headers={"X-API-Key": os.environ["THREADS_API_KEY"]},
json={"url": "https://www.threads.net/@example/post/example", "download_mode": "proxy"},
timeout=15,
)
print(response.json())
Responses
Success response
{
"success": true,
"source": "threads",
"input_url": "https://www.threads.net/@example/post/example",
"normalized_url": "https://www.threads.net/@example/post/example",
"post": { "id": "123", "username": "example", "permalink": "https://www.threads.net/@example/post/example", "text": "Caption" },
"media": [{ "type": "video", "download_url": "https://api.clipmagic.ru/v1/download/dlt_example", "quality": "720p", "duration_seconds": 12.4 }],
"meta": { "request_id": "req_example", "processing_time_ms": 184 }
}
Error response
{ "success": false, "error": { "code": "CONTENT_NOT_ACCESSIBLE", "message": "The Threads post is not publicly accessible or cannot be processed without authorization." }, "meta": { "request_id": "req_example", "processing_time_ms": 122 } }
Error code table
| INVALID_URL | The URL is malformed. |
| INVALID_CURSOR | The comments cursor is expired, tampered, or does not match the URL/limit. |
| REQUEST_TOO_LARGE / UNSUPPORTED_MEDIA_TYPE | HTTP 413 for a body above 32 KiB; HTTP 415 for an unsupported Content-Type. |
| INVALID_QUERY | The search request contains an empty or oversized query, unsupported mode, limit, or field. |
| UNSUPPORTED_DOMAIN | The URL is not a supported Threads domain. |
| VIDEO_NOT_FOUND | No public video was found. |
| CONTENT_NOT_ACCESSIBLE | The post requires login, is private, unavailable or blocked by challenge. |
| SOURCE_TIMEOUT | Threads source did not respond in time. |
| RATE_LIMIT_EXCEEDED | The API key exceeded its configured limit. |
| DOWNLOAD_TOKEN_EXPIRED | The proxy download token expired. |
| DOWNLOAD_TOKEN_CONSUMED | A single-use token has already been used. |
Proxy download flow
Media must belong to the identified post; unrelated or quoted videos are not substituted. If the source cannot establish the identity, resolution fails. Use download_mode=proxy, then call the returned download_url. HEAD /v1/download/:token checks headers without consuming single-use tokens. GET /v1/download/:token streams from the upstream source and enforces byte limits during streaming.
GET Range requests are supported when the upstream source supports them. Every GET and HEAD checks the owner’s current key status and limits, even when no API-key header is required. Revocation blocks new downloads through existing links. Videos are not stored permanently.
Rate limits and beta limitations
Default beta limits are 30 requests per minute, 500 requests per day and 1 GB daily proxy bandwidth unless your key record says otherwise. Credit-mode keys spend one credit after validation. Post, video, comments and search share one request budget. Downloads use a separate shared GET/HEAD budget; HEAD also reserves a concurrent slot. Every comments page and search window is one request. HTTP 429 includes Retry-After. Live Threads extraction reliability depends on public source availability and should be validated with your own public corpus.
FAQ
Does this bypass login or CAPTCHA?
No. It resolves only public or otherwise authorized content and does not use user cookies.
Do proxy links store video files?
No. The proxy streams bytes and enforces limits; it does not permanently store media.
How do I report a bug?
Send the request ID, endpoint, approximate time, error code and URL category to support@clipmagic.ru. Do not send private credentials.
Where is the changelog?
See docs/Status.md in the repository or the deployment changelog maintained by the operator.