# igsub.me Free API (v1.2.0, updated 2026-10-25) > A 100% free (no fees, no paid plans, no API key), public, browser-only JSON API for growing YouTube channels (subscribers, likes, comments, comment likes) and, when enabled, Instagram / TikTok / X / Facebook posts. There is no API key: your web page calls the API directly with fetch() (CORS), the visitor signs in with their own Google account, and every order spends that visitor's free credits. It is the same API our Blogger theme and our own sites use, so you can build a complete site with it: home page with order form, channel page, video page, order tracking page and account page. Every order is protected by hCaptcha. Price: free — no fees, no paid plans, no API key. Base URL: https://igsub.me HTML docs: https://igsub.me/developers OpenAPI 3.1: https://igsub.me/developers/openapi.json Example page (single HTML file): https://igsub.me/developers/starter-kit.html ## Rules - Base URL is always https://igsub.me — every request goes there. All responses are JSON (UTF-8). - Send the header X-FS-Mode: token on every request and never send cookies (credentials: "omit"). - Without sign-in a visitor can find a channel/video and send 100 free YouTube subscribers once per channel (hCaptcha required). Everything else — likes, comments, other platforms, larger quantities, account, credits, history — requires Google sign-in. - After sign-in send Authorization: Bearer on every request. The token is valid 30 days. - Browser only: requests must come from a real web page (the browser sends Origin, Sec-Fetch-Mode: cors and an OPTIONS preflight). Server-side calls (PHP, cURL, Python, Node, bots) get 403 browser_required. - Use POST with Content-Type: application/json for request bodies; unknown body fields are rejected. - hCaptcha is required for every order (body field captcha_token) and for invite codes and channel credits (header X-FS-Captcha). Sign-in has no captcha. You need no captcha key: show https://igsub.me/captcha/frame.html in an iframe (it opens in the visitor's browser language; add ?hl=xx to force one), the visitor solves it there, and you receive the token via postMessage. One token = one call. Never put any secret in your page; none is needed. - Errors are { "kod": "", "error": "", "hata": "" } with a matching HTTP status. Translate on your side by kod. - The Accept-Language header (sent automatically by browsers) selects the language of YouTube titles/descriptions where available. - Accounts and credits are shared: a user who signs in on your site has the same account and balance as on igsub.me. ## Free 100 subscribers (no sign-in) - Any visitor can send 100 free YouTube subscribers without signing in — no account, no credits, no API key. - Step 1: POST /api/resolve { "q": "" } → channel_id (no Authorization header needed). - Step 2: show the hCaptcha frame (https://igsub.me/captcha/frame.html) and wait for the token. - Step 3: POST /api/order { "tip": "sub", "hedef_kanal": "", "captcha_token": "", "fingerprint": "" } → order_id. Quantity is always 100. - Step 4: GET /api/order/{order_id} every 10 s to show live progress (no sign-in needed). - Limits: once per channel (lifetime), 1 free order per hour per visitor (IP + fingerprint), 1 free order per hour per channel, and a shared hourly cap for all external sites. When a limit is hit the API answers 401 giris_gerekli or 429 kanal_cooldown — then offer Google sign-in (free credits, every service). - Only tip "sub" works without sign-in; likes, comments, other platforms and quantities above 100 need sign-in. ## Credits - Without sign-in: 100 free YouTube subscribers once per channel (max 1 free order per hour per visitor). No credits needed. - Credits are free. They are not money and cannot be bought or withdrawn. - Every signed-in user gets free credits every hour (gunluk_hak, normally 1000) plus a welcome bonus on the first sign-in. - Each friend who signs in with the user's invite link/code gives +1000 credits. Invite link: https://igsub.me/?ref= (or pass ref to /api/auth/device/start on your own site). - Each extra YouTube channel on the user's Google account adds +1000 credits per hour (POST /api/kredi/kanal-topla once per hour). - An order costs adet × kredi_carpani credits. If dispatch fails, credits are refunded automatically (iade). - Some services unlock only after a number of invites (min_referans). ## Earn credits with your site (your invite code) - Put your own invite code on your site and earn +1000 credits for every new user who signs in there. - Get your code: sign in on igsub.me (or on your own site) and read referans_kod from GET /api/kredi — it is also shown on the account page. - Send it with every sign-in (no captcha needed): POST /api/auth/device/start?ref=YOURCODE. In the example page just set window.FS_REF = 'YOURCODE'. - If a visitor arrives with someone else's invite link (?ref=…), that link wins; otherwise your code is used. - A user can be invited only once (first invite counts). Limits against abuse: max 30 credited sign-ins per code per day and max 2 per network (IP) per day. - Spend the credits like any user: orders for your own channels on any of our sites. Credits are free and cannot be sold or withdrawn. - Your visitors also get their own invite code and link (https://igsub.me/?ref=THEIRCODE) to earn credits themselves. ## hCaptcha - hCaptcha is required on: POST /api/order (body field captcha_token); POST /api/referans/uygula (header X-FS-Captcha); POST /api/kredi/kanal-topla (header X-FS-Captcha). Sign-in (POST /api/auth/device/start) does not need a captcha. - No captcha key is needed on your site. Show https://igsub.me/captcha/frame.html in an iframe (about 400x560 px). The visitor solves the check there (igsub.me domain, so our site key is valid everywhere). - When solved the frame calls window.parent.postMessage({ fsCaptcha: "" }, "*") from origin https://igsub.me; { fsCaptchaExpired: true } if the token expired before use. Always check event.origin === "https://igsub.me". - A token is valid for one call only; get a new one for each order / invite / credit claim. Missing token → 400 captcha_yok, invalid/used/expired → 400 captcha_ret. - Frame query parameters: hl — Language of the check. Default (omitted or "auto"): the visitor's browser language, so nobody is forced into English. Force one with an hCaptcha language code: en, tr, es, pt, pt-BR, fr, de, it, nl, pl, ru, uk, ar, fa, hi, id, ms, th, vi, ja, ko, zh-CN, zh-TW, ro, cs, el, he, hu, sv, da, fi, no, bg, sr, hr, sk, sl, lt, lv, et and more. Unknown codes fall back to English. | theme — "dark" for a dark check that matches dark mode; default light. ## hCaptcha frame URL: https://igsub.me/captcha/frame.html — show it in an iframe (about 400x560 px). When solved it sends window.parent.postMessage({ fsCaptcha: "" }) from origin https://igsub.me ({ fsCaptchaExpired: true } if it expired). Send the token as captcha_token (POST /api/order) or header X-FS-Captcha (invite code, channel credits; sign-in needs none). One token = one call. Query parameters: - `hl` — Language of the check. Default (omitted or "auto"): the visitor's browser language, so nobody is forced into English. Force one with an hCaptcha language code: en, tr, es, pt, pt-BR, fr, de, it, nl, pl, ru, uk, ar, fa, hi, id, ms, th, vi, ja, ko, zh-CN, zh-TW, ro, cs, el, he, hu, sv, da, fi, no, bg, sr, hr, sk, sl, lt, lv, et and more. Unknown codes fall back to English. - `theme` — "dark" for a dark check that matches dark mode; default light. Example: https://igsub.me/captcha/frame.html?hl=tr&theme=dark ## Sign-in and order flow ``` Visitor Your page (browser) https://igsub.me | click "Sign in" | | |------------------>| POST /api/auth/device/start (no captcha)| | |---------------------------------------->| | |<-- user_code, qr_url, device_token -----| |<-- show code -----| | | opens youtube.com/qr/activate, approves with Google | | | GET /api/auth/device/poll (every 5s) | | | X-FS-Device: device_token | | |---------------------------------------->| | |<-- { status: true, session_token } -----| | | save token -> Authorization: Bearer ... | | paste channel | POST /api/resolve { q } | | |<-- channel_id, videos ------------------| | solves hCaptcha | iframe igsub.me/captcha/frame.html | | |<-- postMessage { fsCaptcha: token } ----| | click "Send" | POST /api/order { tip, hedef_kanal, adet, captcha_token } | |<-- order_id, status: queued ------------| | | GET /api/order/{order_id} (every 10s) | | |<-- status, biten_sayac / adet ----------| ``` ## Quick start (JavaScript, runs in the browser) ```js // igsub.me public API — minimal client (no key, browser only) const API = 'https://igsub.me'; const MY_REF = 'YOURCODE'; // your invite code: +1000 credits for every new user who signs in here let token = localStorage.getItem('fs_token') || ''; async function api(path, { body, headers = {} } = {}) { const h = { 'X-FS-Mode': 'token', ...headers }; if (body) h['Content-Type'] = 'application/json'; if (token) h['Authorization'] = 'Bearer ' + token; const r = await fetch(API + path, { method: body ? 'POST' : 'GET', headers: h, body: body ? JSON.stringify(body) : undefined, mode: 'cors', credentials: 'omit', }); const data = await r.json().catch(() => ({})); if (r.status === 401 && (data.kod === 'oturum_yok' || data.kod === 'kullanici_yok')) { token = ''; localStorage.removeItem('fs_token'); // expired -> sign in again } return { ok: r.ok, status: r.status, data }; } // 1) hCaptcha — solved inside an igsub.me frame (no keys on your site) function captcha() { return new Promise(resolve => { const box = document.createElement('div'); box.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);display:grid;place-items:center;z-index:9999'; box.innerHTML = ''; document.body.appendChild(box); function onMsg(e) { if (e.origin !== API || !e.data || !e.data.fsCaptcha) return; window.removeEventListener('message', onMsg); box.remove(); resolve(e.data.fsCaptcha); } window.addEventListener('message', onMsg); box.onclick = e => { if (e.target === box) { window.removeEventListener('message', onMsg); box.remove(); resolve(''); } }; }); } // 2) Sign in with Google (YouTube TV code) async function signIn(onCode) { const ref = new URLSearchParams(location.search).get('ref') || MY_REF; const s = await api('/api/auth/device/start' + (ref ? '?ref=' + encodeURIComponent(ref) : ''), { body: {} }); // no captcha for sign-in if (!s.ok) throw new Error(s.data.error); onCode(s.data.user_code, s.data.qr_url, s.data.qr_image); // show code + link/QR const until = Date.now() + s.data.expires_in * 1000; while (Date.now() < until) { await new Promise(r => setTimeout(r, Math.max(3, s.data.interval) * 1000)); const p = await api('/api/auth/device/poll', { headers: { 'X-FS-Device': s.data.device_token } }); if (p.data.status) { token = p.data.session_token; localStorage.setItem('fs_token', token); return; } if (['expired', 'no_flow', 'only gmail'].includes(p.data.message)) throw new Error(p.data.message); } throw new Error('expired'); } // 3) Balance const { data: { kredi } } = await api('/api/kredi'); // kredi.kredi = credits // 4) Find a channel and order 100 subscribers const ch = await api('/api/resolve', { body: { q: 'https://www.youtube.com/@channel' } }); const captcha_token = await captcha(); const order = await api('/api/order', { body: { tip: 'sub', hedef_kanal: ch.data.channel_id, adet: 100, captcha_token } }); if (!order.ok) console.log(order.data.kod, order.data.error); // e.g. kredi_yok // 5) Track it const timer = setInterval(async () => { const o = (await api('/api/order/' + order.data.order_id)).data; console.log(o.status, o.biten_sayac + '/' + o.adet); if (['completed', 'canceled', 'failed'].includes(o.status)) clearInterval(timer); }, 10000); ``` ## Endpoints ### Site & catalog Public information. No sign-in needed. #### GET /api/site — Site information Name, languages and public settings of the API host. Call it once on page load if you want to show the brand or language list. Auth: none Response fields: - `domain` (string) — API host domain. - `ad` (string) — Brand name. - `varsayilan_dil` (string) — Default language code. - `diller` (string[]) — Supported language codes. - `secili_dil` (string) — Language picked for this request (Accept-Language). - `telegram_kanal` (string) — Telegram channel link. - `telegram_bot` (string) — Telegram bot link. Response example: ```json { "domain": "igsub.me", "ad": "iGSub", "varsayilan_dil": "en", "diller": ["en", "tr", "es", "pt", "ar", "fr", "de", "ru"], "secili_dil": "en", "telegram_kanal": "https://t.me/...", "telegram_bot": "https://t.me/..." } ``` Note: Other fields (analytics ids, verification tags) belong to igsub.me itself — ignore them. #### GET /api/link-servisler — Service catalog Every service with its limits and credit price. Use it to build the service picker, quantity limits and the cost preview. Hide services where aktif is false. Auth: none Response fields: - `servisler[]` (Service[]) — See the Service model. Response example: ```json { "servisler": [ { "tip": "sub", "platform": "youtube", "tur": "sub", "min_adet": 10, "max_adet": 1000000, "kredi_carpani": 1, "min_referans": 0, "aktif": true, "sira": 0 }, { "tip": "like", "platform": "youtube", "tur": "like", "min_adet": 10, "max_adet": 1000000, "kredi_carpani": 50, "min_referans": 3, "aktif": true, "sira": 6 }, { "tip": "comment", "platform": "youtube", "tur": "comment", "min_adet": 1, "max_adet": 1000000, "kredi_carpani": 10, "min_referans": 100, "aktif": true, "sira": 7 }, { "tip": "comment_like", "platform": "youtube", "tur": "comment_like", "min_adet": 10, "max_adet": 1000000, "kredi_carpani": 15, "min_referans": 5, "aktif": true, "sira": 8 }, { "tip": "ig_views", "platform": "instagram", "tur": "views", "min_adet": 100, "max_adet": 2147483647, "kredi_carpani": 3, "min_referans": 0, "aktif": false, "sira": 1 } ] } ``` Note: cost in credits = quantity × kredi_carpani. Note: min_referans > 0 means the user needs that many invited friends before using the service (error referans_gerekli). ### Authentication Google sign-in with the YouTube TV device code flow. Produces the session token used everywhere else. #### POST /api/auth/device/start — Start Google sign-in Creates a one-time code. Show user_code to the user and send them to qr_url (opens youtube.com/qr/activate with the code pre-filled) or show qr_image to scan with a phone. Keep device_token in memory for polling. Auth: none · Limit: 12 starts per IP per 10 minutes Query parameters: - `ref` (string) — Invite code. Use YOUR code to earn +1000 credits for every new user who signs in on your site (or the visitor's ?ref= link if present). Applied when the sign-in completes; max 30 per code per day. Example: `AB12CD34` Headers: - `X-FS-Mode` (string, required) — Must be "token" so device_token / session_token are returned in JSON (no cookies). Example: `token` Request example: ```json {} ``` Response fields: - `user_code` (string) — Code the user enters (format XXX-XXX-XXX). - `qr_url` (string) — https://youtube.com/qr/activate/ — open in a new tab. - `qr_image` (string) — PNG QR image URL of qr_url. - `verification_url` (string) — Plain activation page (code typed manually). - `interval` (integer) — Seconds to wait between polls (use at least 3). - `expires_in` (integer) — Seconds until the code expires. - `device_token` (string) — Opaque flow token; send it as X-FS-Device when polling. Response example: ```json { "ok": true, "user_code": "ABC-DEF-GHI", "qr_url": "https://youtube.com/qr/activate/ABC-DEF-GHI", "qr_image": "https://api.qrserver.com/v1/create-qr-code/?size=280x280&data=...", "verification_url": "https://www.google.com/device", "interval": 5, "expires_in": 1800, "device_token": "eyJ...signed" } ``` Errors: `rate_limit`, `device_hata` #### GET /api/auth/device/poll — Poll sign-in Call every `interval` seconds until status is true, then save session_token (localStorage) and send it as Authorization: Bearer on every call. Always answers HTTP 200; read status/message. Auth: none Headers: - `X-FS-Mode` (string, required) — "token" Example: `token` - `X-FS-Device` (string, required) — device_token from /api/auth/device/start. Response fields: - `status` (boolean) — true = signed in. - `session_token` (string) — Present when status is true. Valid for 30 days. - `message` (string) — When status is false: "not approved yet" (keep polling), "expired" / "no_flow" (start again), "only gmail" (only @gmail.com accounts), other values = temporary, keep polling. Response example: ```json { "status": false, "message": "not approved yet" } { "status": true, "redirect_url": "/account", "session_token": "eyJ1aWQiOjEy...signed" } ``` Note: New accounts receive a welcome credit bonus. Note: A user who already has an account gets the same account and credits (accounts are shared with igsub.me and the blog). #### GET /api/auth/me — Current user Validate the stored token on page load. 401 oturum_yok / kullanici_yok = token expired or deleted: clear it and show the sign-in button. Auth: Authorization: Bearer required Response fields: - `kullanici.id` (integer) — User id. - `kullanici.email` (string) — Gmail address (show only to the user themself). - `kullanici.ad` (string) — Display name. - `kullanici.avatar` (string) — Avatar URL. - `kullanici.son_giris` (string (date-time)) — Last sign-in. Response example: ```json { "ok": true, "kullanici": { "id": 156, "email": "name@gmail.com", "ad": "Name", "avatar": "https://lh3.googleusercontent.com/...", "son_giris": "2026-10-25T07:55:40Z" } } ``` Errors: `oturum_yok`, `kullanici_yok` #### POST /api/auth/logout — Sign out Tokens are stateless: signing out = deleting session_token from your storage. Calling this endpoint is optional. Auth: none Request example: ```json {} ``` Response fields: - `ok` (boolean) — Always true. Response example: ```json { "ok": true } ``` ### Account & credits The signed-in user, credit balance, credit history, invites and extra-channel credits. #### GET /api/kredi — Credit balance Spendable credits and invite info. Show kredi in the header and refresh it after every order. Auth: Authorization: Bearer required Response fields: - `kredi.kredi` (integer) — Spendable credits (1 credit = 1 unit × kredi_carpani). - `kredi.referans_kod` (string) — User's own invite code. Invite link: https://igsub.me/?ref= - `kredi.davet_sayisi` (integer) — Friends invited so far (unlocks min_referans services). - `kredi.kanal_sayisi` (integer) — Extra YouTube channels found on the account (each adds hourly credits). - `kredi.gunluk_hak` (integer) — Free credits added every hour. - `kredi.sonraki_sifirlama` (string (date-time)) — When the next free credits arrive (UTC). Response example: ```json { "ok": true, "kredi": { "kredi": 1000, "bonus_kredi": 0, "referans_kod": "8E6WNYMQ", "davet_sayisi": 0, "kanal_sayisi": 0, "gunluk_hak": 1000, "sonraki_sifirlama": "2026-10-25T08:00:00Z" } } ``` Errors: `oturum_yok` #### GET /api/kredi/hareketler — Credit history Credit movements, newest first. Auth: Authorization: Bearer required Query parameters: - `limit` (integer) — Default 50. Example: `30` - `offset` (integer) — Pagination offset. Example: `0` Response fields: - `hareketler[]` (CreditTx[]) — See the CreditTx model. Response example: ```json { "ok": true, "hareketler": [ { "id": 991, "tur": "siparis", "miktar": -100, "aciklama": "tip=sub adet=100x1", "order_id": 3927, "olusturma": "2026-10-24T18:05:43Z" }, { "id": 990, "tur": "saatlik", "miktar": 1000, "aciklama": "hourly", "olusturma": "2026-10-24T18:00:00Z" } ] } ``` Errors: `oturum_yok` #### POST /api/referans/uygula — Apply an invite code Manually apply a friend's invite code (the inviter gets +1000 credits). Usually not needed: pass ?ref= to device/start instead. Auth: Authorization: Bearer required Headers: - `X-FS-Captcha` (string, required) — hCaptcha token from the igsub.me captcha frame (one token per call). Body (JSON): - `kod` (string, required) — Invite code. Example: `AB12CD34` Request example: ```json { "kod": "AB12CD34" } ``` Response fields: - `ok` (boolean) — true on success. - `bonus` (integer) — Credits given to the inviter. Response example: ```json { "ok": true, "veren_id": 12, "bonus": 1000 } ``` Errors: `captcha_yok`, `captcha_ret`, `kod_yok`, `referans_gecersiz`, `referans_kendi`, `referans_var`, `referans_limit` #### POST /api/kredi/kanal-topla — Claim extra-channel credits Counts the extra YouTube channels on the user's Google account; every channel adds +1000 credits per hour. Once per hour (when called earlier you get beklemede=true and kalan_saniye). Auth: Authorization: Bearer required Headers: - `X-FS-Captcha` (string, required) — hCaptcha token from the igsub.me captcha frame (one token per call). Request example: ```json {} ``` Response fields: - `kanal_sayisi` (integer) — Extra channels found. - `saatlik_kazanc` (integer) — Credits per hour from channels. - `kredi` (integer) — New balance. - `beklemede` (boolean) — true = called too early. - `kalan_saniye` (integer) — Seconds until it can be called again. Response example: ```json { "ok": true, "kanal_sayisi": 3, "saatlik_kazanc": 3000, "kredi": 4000 } { "ok": true, "kanal_sayisi": 3, "saatlik_kazanc": 3000, "beklemede": true, "kalan_saniye": 2710 } ``` Errors: `captcha_yok`, `captcha_ret`, `oturum_yok`, `kanal_alinamadi` ### YouTube lookup Find channels and videos from any link, @handle or name. Needed to get the channel_id / video_id that orders use. #### POST /api/resolve — Find a channel or video Accepts anything a user may paste: channel URL, @handle, /c/ or /user/ URL, channel name, video URL (watch, youtu.be, shorts) or a bare video id. Returns a channel (tip = "kanal") with its latest videos and similar channels, or a video (tip = "video"). Auth: optional (works without sign-in; send Authorization: Bearer when signed in) · Limit: 40 per minute per IP Body (JSON): - `q` (string, required) — User input. Example: `https://www.youtube.com/@MrBeast` Request example: ```json { "q": "https://www.youtube.com/@MrBeast" } ``` Response fields: - `tip` ("kanal" | "video") — What was found. - `channel_id` (string) — UC… id — use as hedef_kanal in orders. - `ad` (string) — Channel name. - `handle` (string) — Handle without @ (channel only, may be empty). - `avatar` (string) — Channel avatar (channel only). - `abone_sayi` (integer) — Subscriber count (channel only). - `video_sayi` (integer) — Video count (channel only). - `aciklama` (string) — Channel description (channel only). - `videolar[]` ({id, baslik, kapak, bilgi}[]) — Latest videos (channel only). id = video id. - `alternatifler[]` ({channel_id, ad, avatar, abone_sayi}[]) — Similar channels when the input was a name/handle — show "Not this channel?". - `video_id` (string) — Video id (video only). - `video` (object) — Video only: {id, baslik, aciklama, kapak, goruntulenme, sure_sn, yazar}. Response example: ```json { "tip": "kanal", "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "ad": "MrBeast", "handle": "mrbeast", "avatar": "https://yt3.ggpht.com/...", "abone_sayi": 523000000, "video_sayi": 900, "aciklama": "...", "yontem": "handle", "videolar": [ { "id": "dQw4w9WgXcQ", "baslik": "Video title", "kapak": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg", "bilgi": "12M views" } ], "alternatifler": [ { "channel_id": "UC...", "ad": "MrBeast Gaming", "avatar": "...", "abone_sayi": 45000000 } ] } { "tip": "video", "video_id": "dQw4w9WgXcQ", "channel_id": "UCuAXFkgsw1L7xaCfnd5JJOw", "ad": "Channel name", "video": { "id": "dQw4w9WgXcQ", "baslik": "Title", "aciklama": "...", "kapak": "...", "goruntulenme": 1500000000, "sure_sn": 213, "yazar": "Channel name" } } ``` Errors: `bos_girdi`, `cozulemedi`, `giris_gerekli`, `rate_limit` #### GET /api/search — Search channels Channel search by name (max 12). Use for an autocomplete or a "pick your channel" list. Auth: Authorization: Bearer required Query parameters: - `q` (string, required) — Search text. Example: `cooking` Response fields: - `kanallar[]` ({channel_id, ad, avatar, abone_sayi}[]) — Matching channels. Response example: ```json { "kanallar": [ { "channel_id": "UC...", "ad": "Cooking Channel", "avatar": "https://...", "abone_sayi": 120000 } ] } ``` Errors: `bos_girdi`, `giris_gerekli` #### GET /api/channel/{channelId} — Channel page data Full channel card plus the first 30 videos. Use it for a channel page (e.g. #/channel/UC...). Auth: optional (works without sign-in; send Authorization: Bearer when signed in) Path parameters: - `channelId` (string, required) — UC… channel id. Example: `UCX6OQ3DkcsbYNE6H8uQQuVA` Response fields: - `kanal` (Channel) — See the Channel model. - `videolar[]` (Video[]) — See the Video model. Response example: ```json { "kanal": { "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA", "handle": "mrbeast", "ad": "MrBeast", "aciklama": "...", "avatar": "...", "banner": "", "abone_sayi": 523000000, "video_sayi": 900, "ulke": "", "kategori": "" }, "videolar": [ { "video_id": "...", "channel_id": "UC...", "baslik": "...", "kapak": "...", "goruntulenme": 1000000, "sure_sn": 900, "sira": 1 } ] } ``` Errors: `gecersiz_kanal`, `kanal_yok`, `giris_gerekli` #### GET /api/channel/{channelId}/videos — More channel videos / shorts Paginated videos. Default tab uses limit/offset. For Shorts or live streams pass sekme=shorts|streams and page with the returned devam token (continuation). Auth: optional (works without sign-in; send Authorization: Bearer when signed in) Path parameters: - `channelId` (string, required) — UC… channel id. Query parameters: - `limit` (integer) — 1–100, default 30 (videos tab). Example: `30` - `offset` (integer) — Offset (videos tab). Example: `30` - `sekme` ("videos" | "shorts" | "streams") — Tab. Default videos. Example: `shorts` - `continuation` (string) — Next-page token (devam) for shorts/streams. Response fields: - `videolar[]` (Video[]) — Videos (shorts/streams return {video_id, channel_id, baslik, kapak}). - `devam` (string) — Next page token for shorts/streams ("" = end). Response example: ```json { "channel_id": "UC...", "limit": 30, "offset": 30, "videolar": [ ... ] } { "channel_id": "UC...", "sekme": "shorts", "videolar": [ { "video_id": "...", "baslik": "...", "kapak": "..." } ], "devam": "4qmFsgK..." } ``` Errors: `gecersiz_kanal`, `sekme_hata`, `giris_gerekli` #### GET /api/video/{videoId} — Video page data Video details plus its channel card. Use it for a video page with like/comment ordering. Auth: optional (works without sign-in; send Authorization: Bearer when signed in) Path parameters: - `videoId` (string, required) — 11-character video id. Example: `dQw4w9WgXcQ` Response fields: - `video` (object) — {video_id, baslik, aciklama, channel_id, kapak, goruntulenme, sure_sn, etiketler[], yazar} - `kanal` (Channel | {channel_id, ad}) — Channel of the video. Response example: ```json { "video": { "video_id": "dQw4w9WgXcQ", "baslik": "Title", "aciklama": "...", "channel_id": "UC...", "kapak": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg", "goruntulenme": 1500000000, "sure_sn": 213, "etiketler": ["music"], "yazar": "Channel" }, "kanal": { "channel_id": "UC...", "ad": "Channel", "avatar": "...", "abone_sayi": 4000000 } } ``` Errors: `gecersiz_video`, `video_yok`, `giris_gerekli` ### Orders Create orders, follow live progress and list the user's history. #### POST /api/order — Create an order Without sign-in: 100 free YouTube subscribers (tip sub only) once per channel, max 1 free order per hour per visitor, hCaptcha required. Signed in: spends credits (quantity × kredi_carpani) and starts delivery immediately. YouTube services need hedef_kanal (channel_id from /api/resolve); like and comment also need hedef_video. Instagram / TikTok / X / Facebook services need the full post URL in hedef_video and no hedef_kanal. Auth: optional (works without sign-in; send Authorization: Bearer when signed in) · Limit: 30 orders per hour per user (external sites) · 20 per hour per IP Body (JSON): - `tip` (string, required) — Service type from /api/link-servisler: sub, like, comment, comment_like (YouTube) · ig_views, ig_likes, tiktok_views, x_views, fb_likes (only when aktif). Example: `sub` - `hedef_kanal` (string) — YouTube channel_id (UC…). Required for YouTube services. Example: `UCX6OQ3DkcsbYNE6H8uQQuVA` - `hedef_video` (string) — YouTube video id (like, comment, comment_like) or full post URL (Instagram/TikTok/X/Facebook). Example: `dQw4w9WgXcQ` - `adet` (integer) — Quantity. Signed in: clamped to the service min/max and to what the balance can pay (omit = 100). Without sign-in: always 100 (omit it). Example: `100` - `captcha_token` (string, required) — hCaptcha token, required for every order. Get it from the igsub.me captcha frame (see "hCaptcha" section): open https://igsub.me/captcha/frame.html in an iframe and read the token from the postMessage { fsCaptcha }. Single use. - `fingerprint` (string) — Stable device id (random string kept in localStorage). Recommended without sign-in: the free quota is counted per IP + fingerprint. Request example: ```json { "tip": "sub", "hedef_kanal": "UCX6OQ3DkcsbYNE6H8uQQuVA", "adet": 100, "captcha_token": "P1_eyJ0eXAi..." } { "tip": "like", "hedef_kanal": "UCX6OQ3DkcsbYNE6H8uQQuVA", "hedef_video": "dQw4w9WgXcQ", "adet": 50, "captcha_token": "P1_eyJ0eXAi..." } { "tip": "tiktok_views", "hedef_video": "https://www.tiktok.com/@user/video/7300000000000000000", "adet": 1000, "captcha_token": "P1_eyJ0eXAi..." } ``` Response fields: - `order_id` (string) — Unguessable order token. Keep it: it is the key for tracking (share links like #/order/). - `status` (OrderStatus) — Usually "queued"; "failed" if dispatch failed (credits refunded automatically). - `durum` (string) — Same status, internal name. - `adet` (integer) — Final quantity after clamping. Response example: ```json { "ok": true, "order_id": "c43a7ec92aef79f3e7f96c1b55125d44", "durum": "kuyrukta", "status": "queued", "adet": 100 } ``` Errors: `giris_gerekli`, `kanal_cooldown`, `captcha_yok`, `captcha_ret`, `gecersiz_tip`, `hedef_yok`, `link_gecersiz`, `servis_yok`, `kredi_yok`, `min_kredi`, `referans_gerekli`, `rate_limit`, `gecersiz_govde` Note: Every order needs a fresh hCaptcha token (one token = one order). Note: Request body must contain only these fields (unknown fields → 400 gecersiz_govde). Note: Refresh /api/kredi after an order. #### GET /api/order/{order_id} — Order progress Live progress, synced with the delivery engine on every call. Poll every 8–15 seconds while the status is not final. Progress = biten_sayac / adet. Auth: optional (works without sign-in; send Authorization: Bearer when signed in) Path parameters: - `order_id` (string, required) — Token from /api/order. Response fields: - `order_id` (string) — Order token. - `tip` (string) — Service type. - `status` (OrderStatus) — pending | queued | in_progress | completed | canceled | failed. - `adet` (integer) — Ordered quantity. - `baslangic_sayac` (integer) — Counter (subscribers/likes) when delivery started; 0 until known. - `biten_sayac` (integer) — Units delivered so far. - `hedef_kanal` (string) — Target channel. - `hedef_video` (string) — Target video / link. - `olusturma` (string (date-time)) — Created at. - `biten` (string (date-time) | null) — Finished at. Response example: ```json { "order_id": "c43a7ec92aef79f3e7f96c1b55125d44", "tip": "sub", "durum": "calisiyor", "status": "in_progress", "adet": 100, "baslangic_sayac": 18300, "biten_sayac": 40, "hedef_kanal": "UCKtuGDfMjgNe5M9Eu_XsdRw", "hedef_video": "", "olusturma": "2026-10-24T18:05:43Z", "biten": null } ``` Errors: `siparis_yok`, `giris_gerekli` #### GET /api/order/{order_id}/events — Order timeline Step-by-step events for a timeline view. Auth: optional (works without sign-in; send Authorization: Bearer when signed in) Path parameters: - `order_id` (string, required) — Order token. Response fields: - `olaylar[]` (OrderEvent[]) — See the OrderEvent model. Response example: ```json { "order_id": "c43a7ec9...", "olaylar": [ { "id": 1, "olay": "olusturuldu", "detay": "tip=sub adet=100", "ts": "2026-10-24T18:05:43Z" }, { "id": 2, "olay": "kuyruga_alindi", "detay": "ref=...", "ts": "2026-10-24T18:05:44Z" } ] } ``` Errors: `siparis_yok` #### GET /api/siparislerim — My orders The signed-in user's orders on every site, newest first, with the latest event. Auth: Authorization: Bearer required Query parameters: - `limit` (integer) — Default 50. Example: `20` - `offset` (integer) — Pagination offset. Example: `0` Response fields: - `siparisler[]` (OrderSummary[]) — See the OrderSummary model. Link each row with public_token. Response example: ```json { "ok": true, "siparisler": [ { "id": 3927, "public_token": "c43a7ec9...", "platform": "youtube", "tip": "sub", "hedef_kanal": "UC...", "hedef_video": "", "adet": 1000, "durum": "tamamlandi", "status": "completed", "baslangic_sayac": 18300, "biten_sayac": 1000, "son_olay": "kuyruga_alindi", "olusturma": "2026-10-24T18:05:43Z", "biten": "2026-10-24T18:41:02Z" } ] } ``` Errors: `oturum_yok` #### GET /api/siparislerim/{id} — One of my orders Summary plus full event list of one order owned by the user (numeric id from /api/siparislerim). Auth: Authorization: Bearer required Path parameters: - `id` (integer, required) — Numeric order id. Response fields: - `siparis` (OrderSummary) — Order summary. - `olaylar[]` (OrderEvent[]) — Events. Response example: ```json { "ok": true, "siparis": { ... }, "olaylar": [ ... ] } ``` Errors: `gecersiz_id`, `siparis_yok`, `oturum_yok` ### Social proof Recent completed deliveries (masked names) for a live activity feed. #### GET /api/son-siparisler — Recent deliveries Latest completed orders across all sites for a "just delivered" ticker. Names are masked server-side; no e-mail, channel or IP is returned. Cached 30 seconds. Auth: none Response fields: - `siparisler[]` ({tip, adet, ad, saniye_once}[]) — ad = masked name ("" = guest), saniye_once = seconds ago. Response example: ```json { "siparisler": [ { "tip": "sub", "adet": 100, "ad": "Al***", "saniye_once": 42 }, { "tip": "like", "adet": 50, "ad": "", "saniye_once": 95 } ] } ``` ## Data models ### Service Item of /api/link-servisler. - `tip` (string) — Service type used in orders. - `platform` (string) — youtube | instagram | tiktok | x | facebook - `tur` (string) — sub | like | comment | comment_like | views | likes - `min_adet` (integer) — Minimum quantity. - `max_adet` (integer) — Maximum quantity per order. - `kredi_carpani` (integer) — Credits per unit. - `min_referans` (integer) — Invites needed to unlock (0 = open). - `aktif` (boolean) — false = hide it. - `sira` (integer) — Display order. ### Channel YouTube channel card. - `channel_id` (string) — UC… id. - `handle` (string) — Handle without @. - `ad` (string) — Name. - `aciklama` (string) — Description. - `avatar` (string) — Avatar URL. - `banner` (string) — Banner URL (may be empty). - `abone_sayi` (integer) — Subscribers. - `video_sayi` (integer) — Videos. ### Video Video card in channel lists. - `video_id` (string) — Video id. - `channel_id` (string) — Channel id. - `baslik` (string) — Title. - `aciklama` (string) — Description. - `kapak` (string) — Thumbnail URL (fallback: https://i.ytimg.com/vi//mqdefault.jpg). - `goruntulenme` (integer) — Views. - `sure_sn` (integer) — Duration in seconds. - `etiketler` (string[]) — Tags. ### OrderSummary Row of /api/siparislerim. - `id` (integer) — Numeric id (for /api/siparislerim/{id}). - `public_token` (string) — Order token (for /api/order/{order_id}). - `platform` (string) — Platform. - `tip` (string) — Service type. - `hedef_kanal` (string) — Target channel. - `hedef_video` (string) — Target video / link. - `adet` (integer) — Quantity. - `status` (OrderStatus) — English status. - `baslangic_sayac` (integer) — Start count. - `biten_sayac` (integer) — Delivered. - `son_olay` (string) — Latest event name. - `olusturma` (string (date-time)) — Created. - `biten` (string (date-time)) — Finished (omitted while running). ### OrderEvent Timeline item. - `olay` (string) — olusturuldu (created) · kuyruga_alindi (queued for delivery) · teslimat_hata (dispatch error) · kredi_iade (credits refunded) · otomatik_sonlandir (closed automatically) - `detay` (string) — Free text detail. - `ts` (string (date-time)) — Time. ### CreditTx Credit history row. - `tur` (string) — saatlik (hourly free credits) · hosgeldin (welcome bonus) · referans_bonus (invite bonus) · siparis (order, negative) · iade (refund) - `miktar` (integer) — + added / − spent. - `aciklama` (string) — Detail. - `order_id` (integer) — Related order (optional). - `olusturma` (string (date-time)) — Time. ## Order status | status | internal (durum) | meaning | |---|---|---| | pending | beklemede | Created, not yet sent to delivery. | | queued | kuyrukta | Accepted by the delivery engine, waiting to start. | | in_progress | calisiyor | Delivering. biten_sayac grows. | | completed | tamamlandi | Finished (final). | | canceled | iptal | Canceled (final). | | failed | hata | Could not be dispatched; credits refunded (final). | ## Error codes | kod | HTTP | meaning | what to do | |---|---|---|---| | giris_gerekli | 401 | Sign in required (or the free no-sign-in quota is used up for this channel/visitor). | Open the sign-in dialog: signed-in users order with credits. | | kanal_cooldown | 429 | This channel already received a free order in the last hour. | Sign in or try again later. | | oturum_yok | 401 | Token missing, invalid or expired. | Delete the token, ask to sign in again. | | kullanici_yok | 401 | Account no longer exists. | Delete the token. | | browser_required | 403 | Not a real browser request (server-side script). | Call the API from a web page. | | not_public | 403 | Endpoint not available to external sites. | Do not use it. | | captcha_yok | 400 | hCaptcha token missing (captcha_token in /api/order, X-FS-Captcha header for invite code / channel credits). | Show the hCaptcha frame and send its token. | | captcha_ret | 400 | hCaptcha token invalid, expired or already used. | Show the hCaptcha frame again. | | kredi_yok | 402 | Not enough credits. | Show the balance, invite link and next refill time. | | min_kredi | 402 | Balance below the service minimum. | Same as above. | | referans_gerekli | 403 | Service needs more invites ("x/y" in message). | Show the invite link. | | rate_limit | 429 | Too many requests. | Wait and retry with backoff. | | gecersiz_govde | 400 | Invalid JSON or unknown field. | Fix the request body. | | gecersiz_tip | 400 | Unknown service type. | Use a tip from /api/link-servisler. | | hedef_yok | 400 | hedef_kanal missing. | Resolve the channel first. | | link_gecersiz | 400 | Link does not match the platform. | Ask for a valid post URL. | | servis_yok | 400 | Service disabled. | Hide it. | | bos_girdi | 400 | Empty input. | Validate before sending. | | cozulemedi | 422 | Channel/video not found. | Ask for another link. | | kanal_yok | 404 | Channel not found. | — | | video_yok | 404 | Video not found. | — | | siparis_yok | 404 | Order not found. | — | | referans_gecersiz | 404 | Invite code not found. | — | | referans_kendi | 400 | Own invite code. | — | | referans_var | 409 | Invite already used. | — | | referans_limit | 429 | Invite limit for this network/account. | — | | kanal_alinamadi | 502 | Could not read the user's channels. | Ask to sign in again. | | device_hata | 502 | Sign-in could not start. | Retry. | | db_hata | 500 | Temporary server error. | Retry later. | ## Rate limits - All /api calls: 6 requests/second per IP (burst 60). Abusive IPs are banned automatically for 60 minutes. - Heavy calls (resolve, search, order, channel, video, device start): 30 per minute per IP (burst 15). - Per user (external sites): 90 requests per minute. - Orders per user (external sites): 30 per hour, plus the credit balance. - Free orders without sign-in: 100 subscribers once per channel; 1 per hour per visitor (IP + fingerprint); 1 per hour per channel; shared hourly cap for all external sites. - Per site (Origin): 1500 requests per minute. - Sign-in starts: 12 per IP per 10 minutes. - Resolve: 40 per minute per IP. ## Build a complete site (same pages as our Blogger theme) ### Header (every page) 1. On load: if a token is stored call GET /api/auth/me. 200 → show avatar + name; 401 → delete the token, show "Sign in with Google" (no captcha for sign-in). 2. Show the balance from GET /api/kredi (kredi.kredi). Refresh it after each order. 3. Optional: a search box that sends any link/@handle/name to your channel/video page. ### Home page with order form 1. GET /api/link-servisler → build service tabs from items with aktif = true (YouTube: sub, like, comment, comment_like; others when active). 2. Input "channel or video link" → POST /api/resolve. tip = "kanal": show avatar, name, abone_sayi, videolar; tip = "video": show the video card. 3. If alternatifler is not empty show "Not this channel?" with those channels. 4. Quantity input limited by min_adet / max_adet; show cost = adet × kredi_carpani and the user's balance. Lock services where min_referans > davet_sayisi. 5. Send → open the hCaptcha frame (https://igsub.me/captcha/frame.html in an iframe), wait for the postMessage token, then POST /api/order with captcha_token. On success go to the order page with order_id. On error show the message for kod (kredi_yok → invite link + next refill time, captcha_ret → show the check again). 6. Optional live feed: GET /api/son-siparisler every 30 s ("Al*** received 100 subscribers 42 s ago"). ### Channel page (e.g. #/channel/UC…) 1. GET /api/channel/{channelId} → channel header (avatar, banner, abone_sayi, video_sayi, aciklama) + first 30 videos. 2. "Load more": GET /api/channel/{channelId}/videos?offset=30. Shorts tab: ?sekme=shorts then pass devam as continuation. 3. Subscriber order form on top (tip sub, hedef_kanal = channelId); clicking a video opens the video page. ### Video page (e.g. #/video/VIDEO_ID) 1. GET /api/video/{videoId} → title, thumbnail or YouTube embed (https://www.youtube.com/embed/VIDEO_ID), views, duration, channel card. 2. Order buttons for like, comment, comment_like (hedef_kanal = video.channel_id, hedef_video = video id) and sub. ### Order tracking page (e.g. #/order/ORDER_ID) 1. GET /api/order/{order_id} every 10 s → progress bar biten_sayac / adet, status badge, start count. 2. GET /api/order/{order_id}/events → timeline. Stop polling when status is completed, canceled or failed. 3. The order_id link can be shared; it only shows progress, never personal data. ### Account page 1. GET /api/kredi → balance, gunluk_hak, sonraki_sifirlama (countdown), referans_kod (invite link https://igsub.me/?ref=CODE + share buttons), davet_sayisi. 2. Button "Claim channel credits" → hCaptcha frame → POST /api/kredi/kanal-topla with X-FS-Captcha (once per hour). 3. Field "Have an invite code?" → hCaptcha frame → POST /api/referans/uygula with X-FS-Captcha. 4. Tables: GET /api/siparislerim (orders, link to order page with public_token) and GET /api/kredi/hareketler (credit history). 5. Sign out = delete the stored token. ## FAQ **Is the API really free?** Yes. There are no fees, no paid plans and no usage billing — for you and for your visitors. Orders use free credits that refill every hour. **Can I earn credits with my site?** Yes. Pass your invite code to /api/auth/device/start?ref=YOURCODE (or set window.FS_REF in the example page). Every new user who signs in on your site gives you +1000 credits (max 30 per day). **Do I need an API key?** No. There are no keys. The visitor signs in with Google on your page and uses their own credits. **Can I call it from PHP / Node / Python / cURL?** No. Only real browsers are accepted (403 browser_required). Build your page in HTML + JavaScript; the visitor's browser calls the API. **Does it work on Blogger / WordPress / Wix / GitHub Pages?** Yes — anywhere you can add HTML and JavaScript. All calls go to https://igsub.me with CORS. **Can visitors order without signing in on my site?** Yes, but only the free 100 YouTube subscribers: once per channel, max 1 per hour per visitor, with hCaptcha. Likes, comments, other platforms and bigger orders need Google sign-in (free credits). **Is there a captcha?** Yes: every order, invite code and channel-credit claim needs hCaptcha (sign-in does not). It runs inside a small igsub.me frame (https://igsub.me/captcha/frame.html), so you need no captcha keys — you receive the token via postMessage and send it (captcha_token for orders, X-FS-Captcha header for the others). **Which Google accounts work?** @gmail.com accounts. **How long is a session valid?** 30 days. On 401 oturum_yok ask the user to sign in again. **Is there an invite reward?** Yes: every friend who signs in with the user's invite code gives that user +1000 credits. **Which language are responses in?** Errors and status are English (kod is language-neutral). Translate your UI freely. **Is there a sandbox?** No. Use a test channel and small quantities; credits are free. **Where is the machine-readable spec?** OpenAPI 3.1: https://igsub.me/developers/openapi.json — plain text / Markdown for AI assistants: https://igsub.me/developers/llms.txt ## Changelog ### 1.2.0 (2026-10-25) - Sign-in (POST /api/auth/device/start) no longer needs hCaptcha. hCaptcha stays required for orders, invite codes and channel credits. - Admin statistics per external site (requests, sign-ins, orders). ### 1.1.0 (2026-10-25) - hCaptcha is required for every order (captcha_token) and for sign-in start, invite codes and channel credits (X-FS-Captcha header). Solved in the igsub.me frame https://igsub.me/captcha/frame.html — no keys needed. - Site owners can pass their own invite code (?ref=CODE / window.FS_REF) and earn +1000 credits per new user who signs in on their site. - The example page (starter-kit.html) is now identical to our Blogger theme. - Cross-site API is served only from https://igsub.me. - Free without sign-in: 100 YouTube subscribers once per channel (resolve, channel, video, order and tracking work without sign-in; hCaptcha required). - The hCaptcha frame opens in the visitor's browser language by default (?hl=auto); ?hl=xx forces a language. ### 1.0.0 (2026-10-25) - First public release: no API key, browser-only CORS, Google sign-in, English errors (kod + error) and English order status.