czytofejk.pl
For developers

API documentation

Verify content programmatically: images (AI/deepfake), text (fake news, manipulation) and social media videos (with automatic transcription). Analyses use credits from your account — the same as on the website. Generate a key in your account, API tab.

Base URL: https://czytofejk.pl/api/public/v1

Authorization

Every request requires an Authorization header with a key in the czf_… format:

Authorization: Bearer czf_TWOJ_KLUCZ
Key security: the key is shown only once, when it is created (we store only its hash). Use it server-side only — never in browser code or a public repository. Leaked? Revoke the key in your account (takes effect immediately) and create a new one.

Costs in credits

AnalysisCost
Text (up to 1000 characters — the excess is truncated)10–150 credits
Image from a URL (AI / deepfake)10–60 credits
Video from a URL (TikTok, YouTube…) — transcription + analysis10–600 credits
Duplicate (content previously analysed by anyone)0 credits

At the start we hold a deposit (the top of the range) and refund the difference after the analysis — you pay exactly the real cost of the operation. If an analysis fails on our side, the full amount is refunded. The API bills credits only (top-ups and subscriptions: pricing).

Rate limits

60 requests/min per key (Redakcja plan: 300/min) for POST /checks; status and balance reads: 300/min. When exceeded: 429 with a Retry-After header.

POST /checks — start an analysis

JSON body with exactly one of the fields: text (text to verify or a link to a social media video) or image_url (a direct link to an image). Optionally "wait": true — synchronous mode (below).

The optional language field (pl — default, en, uk) sets the language of the verdict body (summary, reasoning, signals). Enum values (PRAWDA | MANIPULACJA | FAŁSZ) stay unchanged regardless of language. Note: dedup is content-based — re-checking identical content returns the existing verdict in the language it was generated in.

Synchronous mode — result in a single response

With "wait": true we hold the request open until the analysis finishes (max ~90 s) and return 200 with the final result — no polling. If the analysis takes longer (typically videos with transcription), you get a regular 202 running and fetch the result with a GET — ideally also with ?wait=true (long-poll). Images and text almost always finish within the budget.

curl — synchronous (the simplest)

curl -X POST https://czytofejk.pl/api/public/v1/checks \
  -H "Authorization: Bearer czf_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{"text": "Rząd potwierdził, że od sierpnia ceny prądu wzrosną o 45%.", "wait": true}'

# → 200 { "id": "…", "status": "done", "result": { "verdict": "FAŁSZ", … } }

curl — asynchronous

curl -X POST https://czytofejk.pl/api/public/v1/checks \
  -H "Authorization: Bearer czf_TWOJ_KLUCZ" \
  -H "Content-Type: application/json" \
  -d '{"text": "Rząd potwierdził, że od sierpnia ceny prądu wzrosną o 45%."}'

# → 202 { "id": "a1b2c3d4e5", "status": "running", … }
# potem (long-poll do wyniku):
curl "https://czytofejk.pl/api/public/v1/checks/a1b2c3d4e5?wait=true" \
  -H "Authorization: Bearer czf_TWOJ_KLUCZ"

JavaScript (Node) — synchronous

const res = await fetch("https://czytofejk.pl/api/public/v1/checks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CZYTOFEJK_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    image_url: "https://example.com/zdjecie.jpg",
    wait: true,
  }),
});
const check = await res.json();
if (check.status === "done") console.log(check.result); // gotowy werdykt

Python — async with long-polling (e.g. videos)

import os, requests

API = "https://czytofejk.pl/api/public/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['CZYTOFEJK_API_KEY']}"}

check = requests.post(f"{API}/checks", headers=HEADERS, json={
    "text": "https://www.tiktok.com/@uzytkownik/video/724..."  # film → transkrypcja
}).json()

# long-poll: każde wywołanie czeka do wyniku albo ~90 s
while check.get("status") == "running":
    check = requests.get(
        f"{API}/checks/{check['id']}", params={"wait": "true"},
        headers=HEADERS, timeout=120,
    ).json()

print(check["result"]["verdict"])

202 response (analysis started)

{
  "id": "a1b2c3d4e5",
  "status": "running",
  "cost_tokens": 1,
  "result_url": "https://czytofejk.pl/w/a1b2c3d4e5"
}

A duplicate returns 200 with "deduped": true and "cost_tokens": 0 (existing result, zero cost). For a new check, cost_tokens is the deposit — the real cost appears in the result (chargedCredits).

GET /checks/{id}status and result

With ?wait=true (recommended) the request waits for the result — max ~90 s per call; repeat until the status is done. Without wait it returns the current state immediately (then poll every ~2 s). Analysis times: images 5–20 s, text 10–30 s, videos with transcription up to a few minutes. Status error = credits refunded automatically.

{
  "id": "a1b2c3d4e5",
  "status": "done",
  "result_url": "https://czytofejk.pl/w/a1b2c3d4e5",
  "result": {
    "kind": "tresc",                 // albo "plik" (obraz)
    "verdict": "MANIPULACJA",        // PRAWDA | MANIPULACJA | FAŁSZ
    "verdictSub": "…",               // 1–2 zdania oceny
    "summary": "…",                  // uzasadnienie
    "signals": [{ "strong": true, "text": "…" }],
    "sources": [{ "domain": "…", "title": "…", "url": "…" }]
    // dla kind="plik": "percent" 0–100 (prawdopodobieństwo AI/fałszerstwa),
    // "verdict" np. "AI" / "AUTENTYCZNE" oraz lista sygnałów technicznych
  }
}

GET /balance — credit balance

{
  "plan": "pro",
  "tokens": { "subscription": 184, "topup": 40, "total": 224 }
}

Error codes

StatuserrorMeaning
401invalid_api_keyMissing/invalid/revoked key
402insufficient_tokensNot enough credits (fields required, balance)
400invalid_inputProvide exactly one field: text or image_url
422text_too_short / not_an_imageThe content is not suitable for analysis
429rate_limitedPer-minute rate limit — wait for Retry-After
404not_foundNo check with this id
500analysis_failedError on our side — try again

Best practices

  • Keep the key in a server-side environment variable; use a separate key per integration (easier rotation and auditing — see the "last used" column in your account).
  • Every result has a stable, public permalink result_url — you can link it for your readers as evidence.
  • Sending the same content multiple times? You lose nothing — dedup responds instantly and for free.
  • The verdict is an evidence-based probability assessment, not a ruling — for editorial decisions, look into signals and sources.

Questions, higher limits, webhooks? Get in touch. We version under /v1 — breaking changes will land under a new path.