API
Read-only JSON (and CSV for exports). Optional X-Api-Key lifts per-IP rate limits and unlocks larger page sizes. CORS open. All responses cache-friendly.
Base URL: https://tgadsspy.com
OpenAPI 3.1 spec: /api/v1/openapi.json — feed it to openapi-typescript, openapi-generator-cli or Postman/Insomnia for a typed client in any language.
Pagination: all list endpoints accept limit and offset. The response meta block carries the running total.
Subscribe to alerts via our bot: @tgadsspybot — use /subscribe crypto or any keyword.
All endpoints work without an API key — you get the ANON tier. Pass X-Api-Key: <your-key> to unlock FREE or PRO limits. Key management lives on /account/api-keys.
| Plan | Rate limit | Max limit | Max offset | Access |
|---|---|---|---|---|
| ANON | 60 req / min (burst) · 1 / s sustained | 50 | 1 000 | no key required |
| FREE | 600 req / min · 10 / s sustained | 100 | 10 000 | free signup |
| PRO | 6 000 req / min · 100 / s sustained | 500 | 100 000 | paid plan |
Send the key in the X-Api-Key header:
curl -H 'X-Api-Key: tgak_••••••••••••••••' \
'https://tgadsspy.com/api/v1/ads?limit=100'Every response carries plan context so you can monitor usage client-side:
| Header | Meaning |
|---|---|
X-Plan | Tier resolved from your key (or ANON). |
X-RateLimit-Limit | Bucket capacity for your tier. |
X-RateLimit-Remaining | Tokens left after this request. |
X-RateLimit-Reset | Seconds until the bucket fully refills. |
X-Request-Id | Trace id for support. Echoed back if you send it in the request, otherwise generated server-side. Include it when reporting issues. |
X-Response-Time-Ms | Server-side latency in milliseconds (gate entry → response). Useful for client-side performance dashboards. |
When the bucket is empty we return HTTP 429. The body includes an upgradeHint pointing at the next tier:
{
"error": "rate_limited",
"plan": "ANON",
"retryAfter": 12,
"upgradeHint": "Get a free API key for 10× higher limits."
}Respect the Retry-After header. If you hit limits repeatedly, upgrade at /account/api-keys.
/rss.xml— RSS feed — latest 50Global RSS feed of the 50 most recent creatives. Also per-niche: /niches/<slug>/rss.xml
curl https://tgadsspy.com/rss.xmlconst r = await fetch('https://tgadsspy.com/rss.xml')
const xml = await r.text()
console.log(xml)import requests
r = requests.get('https://tgadsspy.com/rss.xml')
print(r.text)<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Telegram Ads Spy — latest creatives</title>
<item>
<title>...</title>
<link>https://tgadsspy.com/ads/...</link>
<pubDate>...</pubDate>
<category>crypto</category>
...
</item>
</channel>
</rss>/feed.json— JSON Feed v1.1 — modern aggregator formatSame content stream as /rss.xml but in JSON Feed v1.1 spec (jsonfeed.org). Modern aggregators — Feedbin, Inoreader, NewsBlur, Reeder, Readwise — parse JSON natively. Each item has structured tags array (niche, geo:RU, lang:en, source:TON, cashier flag) для aggregator-side filtering. CORS *. Cache 5 min. CC-BY-4.0.
curl https://tgadsspy.com/feed.jsonconst r = await fetch('https://tgadsspy.com/feed.json')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/feed.json')
print(r.json()){
"version": "https://jsonfeed.org/version/1.1",
"title": "Telegram Ads Spy — latest Telegram ads",
"home_page_url": "https://tgadsspy.com/",
"feed_url": "https://tgadsspy.com/feed.json",
"items": [
{
"id": "https://tgadsspy.com/ads/abc123",
"url": "https://tgadsspy.com/ads/abc123",
"title": "...",
"content_text": "...",
"date_published": "2026-05-06T...",
"tags": ["crypto", "geo:RU", "lang:ru", "source:TON", "cashier"],
"_tgadsspy": { "advertiser": "...", "cta_url": "...", "is_cashier": true }
}
],
"_tgadsspy": { "license": "CC BY 4.0", "rss_alternate": "https://tgadsspy.com/rss.xml" }
}/api/healthz— Health checkCheap liveness probe. Useful for uptime monitors.
curl https://tgadsspy.com/api/healthzconst r = await fetch('https://tgadsspy.com/api/healthz')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/healthz')
print(r.json()){"ok":true,"service":"tgadsspy","version":"0.0.1","ts":"..."}/api/product— Product descriptor (machine-readable)Single-call Telegram Ads Spy descriptor for AI clients and llmstxt.org consumers. Returns live stats (creatives/channels/cashier/mini-apps + glossary count), features narrative, ecosystem block (sister-products with Wall live_stats lazy-block), and data_feeds map. CORS *. Cache 5 min.
curl https://tgadsspy.com/api/productconst r = await fetch('https://tgadsspy.com/api/product')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/product')
print(r.json()){
"name": "tgadsspy",
"tagline": "Telegram Ads Intelligence — public archive of all sponsored ads",
"stats": { "creatives": 36742, "channels": 2046033, "advertisers": 14908, "cashier_creatives": 412, "mini_app_bots": 18247, "..." },
"features": { "creative_archive": "...", "cashier_vertical": "...", "mini_apps": "...", "programmatic_seo": { "glossary_terms": 74, "..." } },
"ecosystem": { "company": "G.media", "sister_products": [{ "name": "Wall", "live_stats": {...}, "..." }] },
"data_feeds": { "ecosystem_api": "https://tgadsspy.com/api/v1/ecosystem", "..." },
"meta": { "schema_version": "1.1", "..." }
}/api/v1/ecosystem— G.Media ecosystem feed (combined)Single envelope returning the entire G.Media product family — replaces two-fetch pattern (/api/product + wall.tg/api/product) for cross-product consumers. Includes legal entity, products (Telegram Ads Spy + Wall) with localised pitches, bots (flagship/assistant/ai/utility), TLD-family domains, live Telegram Ads Spy stats, Wall daily snapshot (cron-mirrored, fallback to WALL_PRODUCT_FACTS if not yet collected). CORS *. Cache 5 min.
curl https://tgadsspy.com/api/v1/ecosystemconst r = await fetch('https://tgadsspy.com/api/v1/ecosystem')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/ecosystem')
print(r.json()){
"ecosystem": { "name": "G.Media", "founder": {...}, "hq": {...} },
"products": [{ "id": "tgadsspy", "pitch": {"en":"...","ru":"..."} }, { "id": "wall", "..." }],
"bots": [{ "handle": "gidbot", "category": "flagship", "..." }, "..."],
"domains": [{ "url": "https://g.media", "canonical": true }, "..."],
"live": {
"tgadsspy": { "stats": { "creatives": 36742, "cashier_creatives": 412, "mini_app_bots": 18247, "..." } },
"wall": { "stats": { "users_total": 70663, "branches_count": 18, "..." } }
},
"data_feeds": { "tgadsspy_*": "...", "wall_*": "..." },
"meta": { "schema_version": "1.0", "license": "CC BY 4.0" }
}/api/v1/glossary— Glossary feed (all terms, EN+RU)Single-call public JSON feed of every Telegram-ads glossary term — 74+ entries with EN+RU localised copy, category and related-term cross-links. Filterable by ?category=ad-types|metrics|technical|... (validated, returns 400 on unknown) or ?slug=a,b,c (regex-validated). Replaces crawling /info/<slug> pages individually. Pure import from lib/glossary.ts, no DB hit. CORS *. Cache 24h.
| Param | Type | Description |
|---|---|---|
category | string | platform | ad-types | metrics | actors | strategies | compliance | technical |
slug | comma-separated | Filter to specific entries. Example: ?slug=telegram-stars,cashier,xtr |
curl "https://tgadsspy.com/api/v1/glossary?category=ad-types"const r = await fetch('https://tgadsspy.com/api/v1/glossary?category=ad-types')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/glossary?category=ad-types')
print(r.json()){
"data": [
{
"slug": "telegram-stars",
"category": "platform",
"url": "https://tgadsspy.com/info/telegram-stars",
"en": { "term": "Telegram Stars (XTR)", "definition": "..." },
"ru": { "term": "Telegram Stars (XTR)", "definition": "..." },
"related": [
{ "slug": "cashier", "url": "https://tgadsspy.com/info/cashier" }
]
}
],
"meta": {
"total": 74,
"categories": ["platform","ad-types","metrics","actors","strategies","compliance","technical"],
"by_category": { "platform": 5, "ad-types": 8, "..." },
"license": "CC BY 4.0",
"source": "lib/glossary.ts"
}
}/api/v1/glossary.csv— Glossary feed — CSV mirrorSame dataset as /api/v1/glossary but in flat 8-column CSV format — slug, category, url, term_en, definition_en, term_ru, definition_ru, related_slugs (pipe-separated). Researcher-friendly for spreadsheet analysis and academic citations. Same filters as JSON. OWASP formula-injection escaped. CORS *. Cache 24h.
| Param | Type | Description |
|---|---|---|
category | string | platform | ad-types | metrics | actors | strategies | compliance | technical |
slug | comma-separated | Filter to specific entries |
curl "https://tgadsspy.com/api/v1/glossary.csv?category=ad-types" > glossary-ad-types.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/glossary.csv?category=ad-types')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/glossary.csv?category=ad-types')
with open('ads.csv', 'wb') as f:
f.write(r.content)slug,category,url,term_en,definition_en,term_ru,definition_ru,related_slugs
telegram-stars,platform,https://tgadsspy.com/info/telegram-stars,Telegram Stars (XTR),"Telegram's native in-app currency...",Telegram Stars (XTR),"Внутренняя валюта Telegram...",cashier|sponsored-invoice|mini-app|sponsored-message
cashier,ad-types,https://tgadsspy.com/info/cashier,Cashier creative,"A sponsored message that triggers a payment flow...",Cashier-креатив,"Спонсорское сообщение, запускающее платёжный flow...",telegram-stars|sponsored-invoice|premium-giveaway|main-app
.../api/v1/stats— Aggregate statsGlobal counts (creatives, channels, advertisers, impressions) plus today-only deltas and payment-source breakdown. Cached 60s.
curl https://tgadsspy.com/api/v1/statsconst r = await fetch('https://tgadsspy.com/api/v1/stats')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/stats')
print(r.json()){
"data": {
"creatives": 1234,
"channels": 156,
"advertisers": 89,
"impressions": 4321,
"paymentBreakdown": { "EUR_ADS_CABINET": 1100, "TON": 134 },
"today": {
"creatives": 48,
"impressions": 92,
"advertisers": 5,
"vsYesterdayPct": 12
},
"ts": "2026-04-19T..."
}
}/api/v1/miniapps— List Telegram mini-appsLeaderboard of bots running main-app mini-apps. Sortable by active users, Telegram official rank, Stars rating level, or D7 trending growth. Filter by niche or sponsored-enabled.
| Param | Type | Description |
|---|---|---|
sort | mau|stars|tgrank|trending | mau = botActiveUsers DESC (default). stars = botStarsRatingLevel DESC. tgrank = popularBotsRank ASC NULLS LAST (Telegram curation). trending = D7 growth % via BotMauSnapshot CTE. |
niche | string | Exact niche slug from INDEXABLE list (crypto, gambling, finance, games, trading, …). Validated server-side, unknown slugs ignored. |
sponsored | 1|true|0|false | When 1/true, return only mini-apps with active Telegram Ads (botSponsoredEnabled=true). |
limit | int 1–500 | Default 50. Max depends on plan: ANON 50, FREE 100, PRO 500. |
offset | int | For pagination. Default 0. |
curl "https://tgadsspy.com/api/v1/miniapps?sort=trending&niche=crypto&limit=20"const r = await fetch('https://tgadsspy.com/api/v1/miniapps?sort=trending&niche=crypto&limit=20')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/miniapps?sort=trending&niche=crypto&limit=20')
print(r.json()){
"data": [
{
"username": "minigame_bot",
"title": "Mini Game",
"avatarUrl": "/m/ab/abc123.jpg",
"botActiveUsers": 1245000,
"botStarsRatingLevel": 12,
"botSponsoredEnabled": true,
"lastBotEnrichmentAt": "2026-05-05T...",
"popularBotsRank": 7,
"botNiche": "games",
"growthPct": 18.4
}
],
"meta": { "total": 132, "limit": 20, "offset": 0, "plan": "FREE", "sort": "trending" }
}/api/v1/miniapps.csv— Mini-app leaderboard — CSV mirror10-column CSV mirror of /api/v1/miniapps for spreadsheet/research workflows. Same filters and sort options (mau/stars/tgrank — no trending CTE for CSV). Requires X-Api-Key — ANON blocked (matches /api/v1/cashier-creatives.csv pattern). OWASP formula-injection escaped. Plan-tier limit cap: FREE 100, PRO 500.
| Param | Type | Description |
|---|---|---|
sort | mau|stars|tgrank | mau = botActiveUsers DESC (default). stars = botStarsRatingLevel DESC. tgrank = popularBotsRank ASC NULLS LAST. |
niche | string | Exact niche slug from INDEXABLE list. |
sponsored | 1|true|0|false | Return only sponsored-enabled mini-apps. |
stars10 | 1|true|0|false | Filter botStarsRatingLevel >= 10. |
limit | int 1–500 | Default 500. Plan caps: FREE 100, PRO 500. |
curl -H "X-Api-Key: $KEY" "https://tgadsspy.com/api/v1/miniapps.csv?sort=stars&niche=crypto" > miniapps-crypto.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/miniapps.csv?sort=stars&niche=crypto')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/miniapps.csv?sort=stars&niche=crypto')
with open('ads.csv', 'wb') as f:
f.write(r.content)username,title,niche,mau,stars_level,popular_bots_rank,sponsored_enabled,avatar_url,last_enriched_at,profile_url
notcoin_bot,Notcoin,crypto,42000000,23,1,true,https://tgadsspy.com/m/ab/abc.jpg,2026-05-06T...,https://tgadsspy.com/miniapps/notcoin_bot
catizen_bot,Catizen,crypto,18500000,18,3,true,...,...,https://tgadsspy.com/miniapps/catizen_bot
.../api/v1/miniapps/<username>— Mini-app detail with time-seriesSingle bot detail: full cashier-signals + last 30 BotMauSnapshot points (takenAt + activeUsers + starsLevel) для researcher sparkline / growth analysis. 404 если bot не enriched (botHasMainApp != true) или username не существует.
| Param | Type | Description |
|---|---|---|
username | string | Bot username (path param). Lowercased, validated against `^[a-z0-9_]{3,32}$`. |
curl "https://tgadsspy.com/api/v1/miniapps/notcoin_bot"const r = await fetch('https://tgadsspy.com/api/v1/miniapps/notcoin_bot')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/miniapps/notcoin_bot')
print(r.json()){
"data": {
"username": "notcoin_bot",
"title": "Notcoin",
"description": "Tap-to-earn TON mini-app",
"avatarUrl": "/m/cd/cd456.jpg",
"botActiveUsers": 42000000,
"botStarsRatingLevel": 23,
"botStarsRatingStars": 1240000,
"botSponsoredEnabled": true,
"popularBotsRank": 1,
"popularBotsRankedAt": "2026-05-05T...",
"botNiche": "crypto",
"lastBotEnrichmentAt": "2026-05-05T...",
"timeseries": [
{ "takenAt": "2026-04-06T...", "activeUsers": 38500000, "starsLevel": 21 },
{ "takenAt": "2026-04-07T...", "activeUsers": 39100000, "starsLevel": 21 }
]
},
"meta": { "plan": "FREE" }
}/api/v1/ads— List creativesPaginated feed of ad creatives. All filters compose; default sort is newest first.
| Param | Type | Description |
|---|---|---|
q | string | Case-insensitive substring match on text or title. |
advertiser | string | Filter by advertiser slug (see /api/v1/advertisers). Returns empty array for unknown slug. |
paymentSource | TON|EUR_ADS_CABINET|UNKNOWN | Exact enum. |
source | ton|eur|all | Shorter alias for paymentSource. |
dest | all|telegram|external | Filter by CTA destination: telegram (t.me links) or external (web URLs). |
niche | string | Exact niche slug (crypto, trading, vpn, …). Granular taxonomy. |
category | finance|gambling|tech|content|commerce|life | Coarse super-group filter — expands to all niches inside. Mutually exclusive with niche (niche wins if both passed). |
geo | string | ISO country code, auto-uppercased. |
lang | string | Creative language hint. |
sort | new|reach | Sort order: new = firstSeenAt desc (default), reach = reachEstimate desc. |
days | int 1–365 | Limit to creatives first seen in the last N days. |
since | ISO timestamp | Creatives first seen after this timestamp (ISO 8601). Used by live polling. |
limit | int 1–500 | Default 50. Max depends on plan: ANON 50, FREE 100, PRO 500. |
offset | int | For pagination. Default 0. |
curl "https://tgadsspy.com/api/v1/ads?niche=crypto&source=eur&limit=20"const r = await fetch('https://tgadsspy.com/api/v1/ads?niche=crypto&source=eur&limit=20')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/ads?niche=crypto&source=eur&limit=20')
print(r.json()){
"data": [
{
"id": "c...",
"title": "P's Trading Journal",
"text": "Private Crypto trading journal...",
"paymentSource": "EUR_ADS_CABINET",
"niche": "crypto",
"category": "finance",
"geo": null,
"lang": null,
"firstSeenAt": "2026-04-18T...",
"lastSeenAt": "2026-04-18T...",
"placementCount": 0,
"impressionCount": 1,
"reachEstimate": "0",
"advertiserId": "...",
"advertiserSlug": "crypto-signals",
"advertiserName": "@cryptosignals",
"mediaUrl": "https://cdn4.telesco.pe/...jpg",
"ctaButtonText": "VIEW BOT",
"ctaUrl": "https://t.me/...",
"targetAvatarUrl": null,
"targetTitle": null
}
],
"meta": { "total": 31, "limit": 20, "offset": 0, "plan": "ANON" }
}/api/v1/ads.csv— Export creatives as CSVSame filters as /api/v1/ads. Returns text/csv with 20 columns (including category between paymentSource and niche; minDisplaySec/maxDisplaySec at the end), up to 500 rows. Attachment download.
| Param | Type | Description |
|---|---|---|
q | string | Same as /api/v1/ads. |
advertiser | string | Filter by advertiser slug. Same as /api/v1/ads. |
source | ton|eur | Same as /api/v1/ads. |
niche | string | Same as /api/v1/ads. |
category | finance|gambling|tech|content|commerce|life | Coarse super-group. Mutually exclusive with niche. |
geo | string | Same as /api/v1/ads. |
limit | int 1–500 | Default 500. |
curl -O "https://tgadsspy.com/api/v1/ads.csv?niche=crypto&limit=100"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/ads.csv?niche=crypto&limit=100')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/ads.csv?niche=crypto&limit=100')
with open('ads.csv', 'wb') as f:
f.write(r.content)id,title,text,paymentSource,category,niche,geo,lang,ctaButtonText,ctaUrl,mediaUrl,advertiser,advertiserSlug,placementCount,impressionCount,reachEstimate,firstSeenAt,lastSeenAt,minDisplaySec,maxDisplaySec
c...,P's Trading Journal,...,EUR_ADS_CABINET,finance,crypto,,,VIEW BOT,https://t.me/...,https://cdn4...jpg,,,0,1,0,2026-04-18T...,2026-04-18T...,5,30/api/v1/cashier-creatives— List cashier creatives (Stars / giveaway)Pre-filtered feed of cashier creatives — Telegram Stars инвойсы (XTR + classic currencies) и Premium giveaway / giveaway_results. Symmetric pair to /api/v1/miniapps (Channel-side cashier surface). Default sort newest first; meta.byType returns invoice/giveaway counts under the active filter.
| Param | Type | Description |
|---|---|---|
type | invoice|giveaway | Discriminator. Omit for both. invoice = OR(invoiceCurrency NOT NULL, mediaType=invoice). giveaway = OR(giveawayPrize NOT NULL, mediaType IN giveaway/giveaway_results). |
currency | string | 3–4 uppercase letters. XTR = Telegram Stars, иначе ISO 4217 (USD, EUR, RUB, …). Validates server-side. |
advertiser | string | Filter by advertiser slug (see /api/v1/advertisers). Returns empty array for unknown slug. |
niche | string | Niche slug (crypto, gambling, finance, games, trading, …). |
geo | string | Advertiser-intent geo (ISO-3166 alpha-2). |
serv | string | SponsoredImpression.region (audience served-to). Distinct from `geo`. |
lang | string | Creative language (en, ru, …). |
sort | new|reach | new = firstSeenAt DESC (default). reach = reachEstimate DESC. |
days | int 1–365 | Recent window in days. ANON clamped to ≤7. |
since | ISO timestamp | Cursor for live polling. ANON clamped to last 24h. |
limit | int 1–500 | Default 50. Max depends on plan: ANON 50, FREE 100, PRO 500. |
offset | int | For pagination. Default 0. |
curl "https://tgadsspy.com/api/v1/cashier-creatives?type=invoice¤cy=XTR&limit=20"const r = await fetch('https://tgadsspy.com/api/v1/cashier-creatives?type=invoice¤cy=XTR&limit=20')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/cashier-creatives?type=invoice¤cy=XTR&limit=20')
print(r.json()){
"data": [
{
"id": "c...",
"title": "Premium Stars Pack",
"text": "Get 1000 stars instantly",
"paymentSource": "TON",
"niche": "crypto",
"geo": null,
"lang": "en",
"isCashierCreative": true,
"invoiceCurrency": "XTR",
"invoiceTotalAmount": "1000",
"invoiceTitle": "Stars Pack",
"giveawayPrize": null,
"giveawayMonths": null,
"ctaButtonText": "BUY",
"ctaUrl": "https://t.me/example_bot?start=stars",
"ctaTargetUsername": "example_bot",
"advertiserSlug": "stars-shop",
"advertiserName": "Stars Shop",
"reachEstimate": "12500",
"firstSeenAt": "2026-05-04T...",
"lastSeenAt": "2026-05-05T..."
}
],
"meta": {
"total": 47,
"byType": { "invoice": 32, "giveaway": 15 },
"limit": 20,
"offset": 0,
"plan": "FREE"
}
}/api/v1/cashier-creatives.csv— Export cashier creatives as CSVSame filters as /api/v1/cashier-creatives. Returns text/csv with 27 columns (extends ads.csv с invoice* + giveaway* + ctaTargetUsername полями), up to 500 rows. Attachment download. Requires X-Api-Key (FREE+ plan, anon blocked).
| Param | Type | Description |
|---|---|---|
type | invoice|giveaway | Same as /api/v1/cashier-creatives. |
currency | string | XTR or ISO 4217. Same as /api/v1/cashier-creatives. |
advertiser | string | Filter by advertiser slug. |
niche | string | Niche slug. |
geo | string | Advertiser-intent geo. |
serv | string | Audience served-to region. |
lang | string | Creative language. |
limit | int 1–500 | Default 500. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/cashier-creatives.csv?type=invoice¤cy=XTR&limit=100"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/cashier-creatives.csv?type=invoice¤cy=XTR&limit=100')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/cashier-creatives.csv?type=invoice¤cy=XTR&limit=100')
with open('ads.csv', 'wb') as f:
f.write(r.content)id,title,text,paymentSource,niche,geo,lang,isCashierCreative,invoiceCurrency,invoiceTotalAmount,invoiceTitle,giveawayPrize,giveawayMonths,giveawayWinnersCount,giveawayUntilDate,giveawayCountriesIso2,ctaButtonText,ctaUrl,ctaTargetUsername,mediaUrl,advertiser,advertiserSlug,placementCount,impressionCount,reachEstimate,firstSeenAt,lastSeenAt
c...,Premium Stars Pack,Get 1000 stars,TON,crypto,,en,true,XTR,1000,Stars Pack,,,,,,BUY,https://t.me/example_bot,example_bot,,,,0,1,12500,2026-05-04T...,2026-05-05T.../api/v1/channels— List channelsTelegram channels we track, sorted by member count desc.
| Param | Type | Description |
|---|---|---|
q | string | Case-insensitive substring match on username or title. |
lang | string | Channel language filter. |
category | string | Category filter (news, crypto, …). |
minMembers | int | Only channels with members ≥ this value. |
sponsored | eligible | Only channels eligible for Telegram sponsored messages. |
limit | int 1–500 | Default 50. Max depends on plan: ANON 50, FREE 100, PRO 500. |
offset | int | Default 0. |
curl "https://tgadsspy.com/api/v1/channels?sponsored=eligible&minMembers=100000"const r = await fetch('https://tgadsspy.com/api/v1/channels?sponsored=eligible&minMembers=100000')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/channels?sponsored=eligible&minMembers=100000')
print(r.json()){
"data": [
{
"id": "...",
"username": "durov",
"title": "Pavel Durov",
"lang": "en",
"category": "personal",
"members": 10248881,
"avgViews": 819910,
"estimatedCpm": null,
"isVerified": true,
"isSponsoredEligible": true,
"lastSnapshotAt": "2026-04-19T..."
}
],
"meta": { "total": 108, "limit": 50, "offset": 0 }
}/api/v1/advertisers— List advertisersAdvertisers we identified from CTA URLs (domains + Telegram handles). Default sort: creative count desc.
| Param | Type | Description |
|---|---|---|
q | string | Case-insensitive match on name, slug, or domain. |
domain | string | Partial domain match (case-insensitive). |
tgUsername | string | Exact Telegram username match (case-insensitive). Use without @ prefix. |
sort | popular|new|recent | Sort: popular = most creatives (default), new = firstSeenAt desc, recent = lastSeenAt desc. |
limit | int 1–500 | Default 50. Max depends on plan: ANON 50, FREE 100, PRO 500. |
offset | int | Default 0. |
curl "https://tgadsspy.com/api/v1/advertisers?q=crypto&limit=20"const r = await fetch('https://tgadsspy.com/api/v1/advertisers?q=crypto&limit=20')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/advertisers?q=crypto&limit=20')
print(r.json()){
"data": [
{
"id": "...",
"slug": "tg-mrp_tradingbot",
"name": "@MrP_Tradingbot",
"domain": null,
"tgUsername": "mrp_tradingbot",
"creativeCount": 1,
"firstSeenAt": "2026-04-18T...",
"lastSeenAt": "2026-04-18T..."
}
],
"meta": { "total": 10, "limit": 20, "offset": 0 }
}/api/v1/advertisers.csv— Advertiser index — CSV mirror9-column CSV mirror of /api/v1/advertisers for competitive intelligence workflows. Sortable by popular/new/recent. Filterable by q/domain/niche. Requires X-Api-Key — ANON blocked (matches /api/v1/cashier-creatives.csv pattern). OWASP escape. Plan-tier limit cap: FREE 100, PRO 500.
| Param | Type | Description |
|---|---|---|
q | string | Case-insensitive search (name/slug/domain). |
domain | string | Partial domain filter. |
niche | string | Exact primary niche slug. |
sort | popular|new|recent | Default popular. |
limit | int 1–500 | Default 500. Plan caps: FREE 100, PRO 500. |
curl -H "X-Api-Key: $KEY" "https://tgadsspy.com/api/v1/advertisers.csv?niche=crypto&sort=popular" > advertisers-crypto.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/advertisers.csv?niche=crypto&sort=popular')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/advertisers.csv?niche=crypto&sort=popular')
with open('ads.csv', 'wb') as f:
f.write(r.content)slug,name,domain,tg_username,primary_niche,creative_count,first_seen_at,last_seen_at,profile_url
ftmo,FTMO,ftmo.com,ftmo,prop-firm,1240,2026-01-15T...,2026-05-06T...,https://tgadsspy.com/advertisers/ftmo
binance,Binance,binance.com,binance,crypto,890,2026-02-10T...,2026-05-06T...,https://tgadsspy.com/advertisers/binance
.../api/v1/advertisers/:slug— Get advertiser by slugLook up a single advertiser by their unique slug. Returns 404 for unknown slugs. Includes: primary niche, honeypot flag (anti-scrape Phase B decoy detection), discovery aggregates (channels promoting CTAs, t.me-post mention count, discovery sources), и domains[] array (all known domain mappings — main + .ru + .com + landing-pages).
| Param | Type | Description |
|---|
curl "https://tgadsspy.com/api/v1/advertisers/tg-mrp_tradingbot"const r = await fetch('https://tgadsspy.com/api/v1/advertisers/tg-mrp_tradingbot')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/advertisers/tg-mrp_tradingbot')
print(r.json()){
"data": {
"id": "...",
"slug": "tg-mrp_tradingbot",
"name": "@MrP_Tradingbot",
"domain": null,
"tgUsername": "mrp_tradingbot",
"creativeCount": 1,
"firstSeenAt": "2026-04-18T...",
"lastSeenAt": "2026-04-18T...",
"primaryNiche": "trading",
"isHoneypot": false,
"channelsPromotedIn": 12,
"tmePostsCount": 47,
"discoverySources": ["tme-scraper", "gramesh-cta"],
"domains": [
{
"domain": "mrp-trading.com",
"rootDomain": "mrp-trading.com",
"firstSeenAt": "2026-04-18T...",
"lastSeenAt": "2026-05-17T...",
"postsCount": 38,
"channelsCount": 9,
"source": "tme-scraper"
}
]
}
}/api/v1/channels/:username/aliases— Fragment NFT-username aliases для каналаTelegram через Fragment marketplace (TON) разрешает на одном entity несколько usernames (multinames). Owner может attach/detach/sell aliases. Этот endpoint возвращает все известные variants — включая detached (с detachedAt set) для historical record. Resolves username через canonical Channel ИЛИ alias mapping. Use cases: brand monitoring, anti-squatting detection, Fragment marketplace research.
| Param | Type | Description |
|---|
curl "https://tgadsspy.com/api/v1/channels/durov/aliases"const r = await fetch('https://tgadsspy.com/api/v1/channels/durov/aliases')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/channels/durov/aliases')
print(r.json()){
"data": [
{
"alias": "durov",
"source": "user-submit",
"firstSeenAt": "2026-01-15T...",
"lastSeenAt": "2026-05-17T...",
"detachedAt": null
},
{
"alias": "old_durov",
"source": "gramesh-multiname",
"firstSeenAt": "2025-08-01T...",
"lastSeenAt": "2026-01-15T...",
"detachedAt": "2026-01-15T..."
}
],
"meta": { "canonical": "durov", "total": 2, "plan": "FREE" }
}/api/v1/niches— List niches with countsAll known niches (crypto, trading, vpn, gambling, tech, news, retail, finance, gaming, education, bots) with current counts and SEO-friendly metadata. Useful for building navigation or a niche directory.
curl https://tgadsspy.com/api/v1/nichesconst r = await fetch('https://tgadsspy.com/api/v1/niches')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/niches')
print(r.json()){
"data": [
{
"slug": "crypto",
"title": "Crypto ads on Telegram",
"headline": "Crypto, Web3 and token ads",
"description": "Every Bitcoin, Ethereum, TON...",
"count": 12,
"indexable": true,
"url": "https://tgadsspy.com/niches/crypto",
"feed": "https://tgadsspy.com/niches/crypto/rss.xml"
}
],
"meta": { "total": 11, "locale": "en" }
}/api/v1/categories— List categories (coarse taxonomy) with niches insideCoarse-grained taxonomy: 6 super-groups (Finance, Gambling, Tech, Content, Commerce, Lifestyle). Each item includes per-group total + niches[] with counts. Companion to /api/v1/niches at one level up. Use ?category=<group> as a filter on /api/v1/ads or /api/v1/cashier-creatives to expand to all niches inside.
curl https://tgadsspy.com/api/v1/categoriesconst r = await fetch('https://tgadsspy.com/api/v1/categories')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/categories')
print(r.json()){
"data": [
{
"slug": "finance",
"label": "Finance & Crypto",
"total": 1240,
"nicheCount": 4,
"niches": [
{ "slug": "crypto", "count": 850 },
{ "slug": "trading", "count": 220 },
{ "slug": "finance", "count": 130 },
{ "slug": "prop-firm", "count": 40 }
],
"url": "https://tgadsspy.com/categories/finance"
}
],
"meta": { "total": 6, "locale": "en" }
}/api/v1/categories.csv— Categories taxonomy as flat CSVFlat denormalized CSV — one row per (category, niche) pair with niche_count + category_total. Suitable for Excel pivots and pandas import without JSON nesting.
curl https://tgadsspy.com/api/v1/categories.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/categories.csv')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/categories.csv')
with open('ads.csv', 'wb') as f:
f.write(r.content)category_slug,category_label,niche_slug,niche_count,category_total
finance,Finance & Crypto,crypto,850,1240
finance,Finance & Crypto,trading,220,1240
finance,Finance & Crypto,finance,130,1240
finance,Finance & Crypto,prop-firm,40,1240
.../api/v1/niches.csv— Niche distribution — CSV mirror8-column CSV mirror of /api/v1/niches for spreadsheet/research workflows. Adds advertiser_count column для diversity analysis (количество distinct advertisers per niche). Researcher-friendly for ad-market vertical evolution studies. Public, no auth, 60s cache.
curl "https://tgadsspy.com/api/v1/niches.csv" > niches.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/niches.csv')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/niches.csv')
with open('ads.csv', 'wb') as f:
f.write(r.content)slug,title,headline,count,advertiser_count,indexable,url,feed_url
crypto,Crypto ads on Telegram,"Crypto, Web3 and token ads",12450,1248,true,https://tgadsspy.com/niches/crypto,https://tgadsspy.com/niches/crypto/rss.xml
trading,Trading signals,"Trading signal services",8900,890,true,https://tgadsspy.com/niches/trading,https://tgadsspy.com/niches/trading/rss.xml
.../api/v1/geos— List geos with countsCountries we have classified creatives for, based on CTA URL TLD and language hints. Sorted by creative count descending.
curl https://tgadsspy.com/api/v1/geosconst r = await fetch('https://tgadsspy.com/api/v1/geos')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/geos')
print(r.json()){
"data": [
{ "code": "RU", "count": 4 },
{ "code": "IT", "count": 1 }
],
"meta": { "total": 2 }
}/api/v1/geos.csv— Geo distribution — CSV mirror5-column CSV mirror of /api/v1/geos (code, name, count, intent_count, profile_url). Dual-signal pattern: count = audience-delivery (regional observer pool — internal volunteer network) vs intent_count = advertiser intent (creative content-derived: TLD + script + language). Researcher-friendly format for academic geographic ad-market studies. Public, no auth, 300s cache.
curl "https://tgadsspy.com/api/v1/geos.csv" > geos.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/geos.csv')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/geos.csv')
with open('ads.csv', 'wb') as f:
f.write(r.content)code,name,count,intent_count,profile_url
RU,Russia,12450,8900,https://tgadsspy.com/countries/ru
TR,Türkiye,5200,3100,https://tgadsspy.com/countries/tr
IN,India,4100,2400,https://tgadsspy.com/countries/in
.../api/v1/comparisons— List vs-pages (pairs, trios, niche-pairs, geo-pairs)Programmatic access to all Wave-78/79 comparison pages. type=advertiser-pair returns 2-way brand comparisons (e.g. ftmo-vs-myfundedfx); type=advertiser-trio returns 3-way trifecta (top-3 per niche); type=niche-pair returns niche-vs-niche taxonomy pages (e.g. crypto-vs-gambling); type=geo-pair returns country-vs-country market pages (e.g. us-vs-de). Optional filters: ?niche=crypto (restrict by niche), ?advertiser=ftmo (find comparisons containing this advertiser), ?locale=ru (translate niche labels). Backed by 10-min Redis cache (same data as sitemap.xml).
| Param | Type | Description |
|---|---|---|
type | advertiser-pair|advertiser-trio|niche-pair|geo-pair | Comparison kind. Default advertiser-pair. |
limit | int 1–100 | 1-100 entries. Default 50. |
niche | string (niche slug) | For advertiser-pair/trio: filter by primary niche. For niche-pair: pairs containing this niche. |
advertiser | string (advertiser slug) | For advertiser-pair/trio: returns only entries containing this advertiser. |
locale | en|ru|uk|it|es|pt|de|fr|ar|id | Locale for label rendering. Default en. |
curl "https://tgadsspy.com/api/v1/comparisons?type=advertiser-pair&niche=prop-firm&advertiser=ftmo&limit=5"const r = await fetch('https://tgadsspy.com/api/v1/comparisons?type=advertiser-pair&niche=prop-firm&advertiser=ftmo&limit=5')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/comparisons?type=advertiser-pair&niche=prop-firm&advertiser=ftmo&limit=5')
print(r.json()){
"data": [
{
"type": "advertiser-pair",
"canonical": "ftmo-vs-myfundedfx",
"members": [
{ "slug": "ftmo", "name": "FTMO" },
{ "slug": "myfundedfx", "name": "MyFundedFX" }
],
"niche": "prop-firm",
"url": "https://tgadsspy.com/advertisers/vs/ftmo-vs-myfundedfx"
}
],
"meta": {
"type": "advertiser-pair",
"total": 5,
"limit": 5,
"locale": "en",
"filters": { "niche": "prop-firm", "advertiser": "ftmo" },
"plan": "FREE"
}
}/api/v1/comparisons/stats— Vs-vertical meta-stats (totals + top entities)Single endpoint для dashboard headline tiles. Returns total comparison count per kind, top-10 advertisers by appearance в pairs/trios, top-10 niches by combined appearance, top-10 countries by geo-pair appearance. Cheap (reads cached pools — 1-2 batch Advertiser query). 5-min cache.
curl https://tgadsspy.com/api/v1/comparisons/statsconst r = await fetch('https://tgadsspy.com/api/v1/comparisons/stats')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/comparisons/stats')
print(r.json()){
"data": {
"totals": {
"advertiser-pair": 100,
"advertiser-trio": 28,
"niche-pair": 47,
"geo-pair": 35,
"all": 210
},
"topAdvertisers": [
{ "slug": "ftmo", "name": "FTMO", "pairCount": 8, "trioCount": 3, "total": 11 },
...
],
"topNiches": [
{ "slug": "prop-firm", "label": "Prop firm", "nichePairCount": 4, "advertiserContextCount": 24, "total": 28 },
...
],
"topCountries": [
{ "iso": "US", "name": "United States", "pairCount": 14 },
...
],
"generatedAt": "2026-05-18T..."
},
"meta": { "plan": "FREE" }
}/api/v1/comparisons/:type/:canonical— Vs-page deep metrics (full payload)Companion к list endpoint — returns same data структура что vs-page renders (advertiser/niche/country metadata + creative counts + 90d advertiser counts + overlap arrays). 404 если pair fails filter rules (same gate as page). 5-min cache.
| Param | Type | Description |
|---|---|---|
type | advertiser-pair|advertiser-trio|niche-pair|geo-pair | Path segment, required. |
canonical | string | Lex-sorted slug. Non-canonical orderings return 404 (no implicit redirect on API). |
curl https://tgadsspy.com/api/v1/comparisons/advertiser-pair/ftmo-vs-myfundedfxconst r = await fetch('https://tgadsspy.com/api/v1/comparisons/advertiser-pair/ftmo-vs-myfundedfx')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/comparisons/advertiser-pair/ftmo-vs-myfundedfx')
print(r.json()){
"data": {
"type": "advertiser-pair",
"canonical": "ftmo-vs-myfundedfx",
"url": "https://tgadsspy.com/advertisers/vs/ftmo-vs-myfundedfx",
"advertisers": [
{
"slug": "ftmo",
"name": "FTMO",
"domain": "ftmo.com",
"creativeCount": 248,
"firstSeenAt": "2024-09-15T...",
"lastSeenAt": "2026-05-17T...",
"topNiche": "prop-firm"
},
{ "slug": "myfundedfx", "name": "MyFundedFX", ... }
],
"niche": "prop-firm"
},
"meta": { "type": "advertiser-pair", "canonical": "ftmo-vs-myfundedfx", "plan": "FREE" }
}/api/v1/comparisons.csv— Vs-pages — CSV mirror (researcher-friendly)10-column CSV mirror of /api/v1/comparisons. Default type=all combines all 4 kinds в один dataset (researcher cross-cut analysis). Same ?niche= и ?advertiser= filters как JSON sister (filter applied post-fetch с pool widening). Empty member_3_* для pair/niche-pair/geo-pair (только trio has 3 members). OWASP formula-injection escaped. Public, no auth, 60s cache.
| Param | Type | Description |
|---|---|---|
type | advertiser-pair|advertiser-trio|niche-pair|geo-pair|all | Default all (combines all 4 kinds). |
limit | int 1–100 | Per-kind cap. Default 50. |
niche | string | For advertiser-*: Advertiser.primaryNiche filter. For niche-pair: pairs containing this slug. |
advertiser | string | For advertiser-pair/trio: returns rows containing this slug. |
curl "https://tgadsspy.com/api/v1/comparisons.csv?type=all&limit=50" > comparisons.csvimport { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/comparisons.csv?type=all&limit=50')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/comparisons.csv?type=all&limit=50')
with open('ads.csv', 'wb') as f:
f.write(r.content)type,canonical,member_1_slug,member_1_name,member_2_slug,member_2_name,member_3_slug,member_3_name,niche,url
advertiser-pair,ftmo-vs-myfundedfx,ftmo,FTMO,myfundedfx,MyFundedFX,,,prop-firm,https://tgadsspy.com/advertisers/vs/ftmo-vs-myfundedfx
advertiser-trio,ftmo-vs-fundednext-vs-myfundedfx,ftmo,FTMO,fundednext,FundedNext,myfundedfx,MyFundedFX,prop-firm,https://tgadsspy.com/advertisers/vs/ftmo-vs-fundednext-vs-myfundedfx
niche-pair,crypto-vs-gambling,crypto,Crypto,gambling,Gambling,,,,https://tgadsspy.com/niches/vs/crypto-vs-gambling
geo-pair,de-vs-us,de,Germany,us,United States,,,,https://tgadsspy.com/countries/vs/de-vs-us
.../api/v1/trends— Time series — creatives per dayNew creatives counted per UTC day, pivoted on a dimension. Useful for charts of niche growth, category mix, payment-source split or geo breakdown over time. dim=category aggregates niches into 6 super-groups (Finance/Gambling/Tech/Content/Commerce/Lifestyle).
| Param | Type | Description |
|---|---|---|
dim | niche|category|source|geo | What to group by. Default niche. category aggregates niches into 6 super-groups. |
days | int 1–90 | Lookback window. Default 30. |
curl "https://tgadsspy.com/api/v1/trends?dim=niche&days=30"const r = await fetch('https://tgadsspy.com/api/v1/trends?dim=niche&days=30')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/trends?dim=niche&days=30')
print(r.json()){
"data": [
{ "date": "2026-04-19", "series": { "crypto": 8, "trading": 4, "vpn": 2 } },
{ "date": "2026-04-20", "series": { "crypto": 10, "trading": 3 } }
],
"meta": {
"dim": "niche",
"days": 30,
"since": "2026-03-20T00:00:00Z",
"keys": ["crypto","trading","vpn"],
"totals": { "crypto": 18, "trading": 7, "vpn": 2 }
}
}/api/v1/analytics— Analytics catalog (machine-readable)Index endpoint listing all analytics surfaces with path/method/summary/params/csvMirror/plans/status. Useful for SDK / dashboard builders that auto-discover capabilities. No auth required.
| Param | Type | Description |
|---|
curl "https://tgadsspy.com/api/v1/analytics" | jq '.endpoints[] | select(.status == "live") | .path'const r = await fetch('https://tgadsspy.com/api/v1/analytics')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics')
print(r.json()){
"endpoints": [
{
"path": "/api/v1/analytics/share-of-voice",
"method": "GET",
"summary": "...",
"params": ["niche", "geo", "top", "from", "to"],
"csvMirror": "/api/v1/analytics/share-of-voice.csv",
"plans": ["FREE", "PRO", "ENTERPRISE"],
"status": "live"
},
...
],
"meta": {
"version": "phase-2",
"totalEndpoints": 15,
"liveEndpoints": 12,
"stubEndpoints": 3,
"csvMirrors": 12,
"specUrl": "/api/v1/openapi.json",
"docsUrl": "/api-docs"
}
}/api/v1/feeds— Feeds catalog (single discovery anchor)Machine-readable catalog of all 22+ data feeds and datasets. Each entry has structured metadata: format (json/csv/xml/feed+json/rss+xml/opensearch/text), plan (ANON/FREE/PRO/ENTERPRISE), cache_ttl_sec, auth_required, license, tags array, schema_url. SDK builders use this as single discovery anchor вместо scraping data_feeds blocks across /api/product and /api/v1/ecosystem. Filterable.
| Param | Type | Description |
|---|---|---|
format | string | json | csv | xml | feed+json | rss+xml | opensearch | text |
plan | string | ANON | FREE | PRO | ENTERPRISE |
tag | string | cashier, miniapps, glossary, ecosystem, researcher, sdk, ai-crawler, … |
curl "https://tgadsspy.com/api/v1/feeds?format=csv&plan=FREE" | jq '.data[].url'const r = await fetch('https://tgadsspy.com/api/v1/feeds?format=csv&plan=FREE')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/feeds?format=csv&plan=FREE')
print(r.json()){
"data": [
{
"id": "api-v1-cashier-creatives-csv",
"name": "Cashier Creatives — CSV",
"url": "https://tgadsspy.com/api/v1/cashier-creatives.csv",
"format": "csv",
"description": "27-column CSV export...",
"plan": "FREE",
"cache_ttl_sec": 60,
"auth_required": true,
"license": "CC-BY-4.0",
"tags": ["cashier", "creatives", "csv", "researcher"]
}
],
"meta": {
"total": 8,
"total_unfiltered": 22,
"by_format": { "csv": 4, "json": 14, "..." },
"by_plan": { "ANON": 17, "FREE": 5 },
"by_tag": { "cashier": 4, "researcher": 4, "..." },
"filters_applied": { "format": "csv", "plan": "FREE", "tag": null }
}
}/api/v1/analytics/* (Phase 2)— Analytics endpoints — Phase 2 (10 endpoints, 7 with real compute)Pro+ analytics surface. Phase 1 endpoints (real compute): /share-of-voice (SoV by advertiser in niche), /creative-fatigue (peak detection + fatigueIndex per creative), /frequency-distribution (imps/unique-channel histogram + p50/p90), /competitor-overlap (Jaccard half-matrix for 2-5 advertisers across channels/niches/creatives), /spend-estimate (groupBy advertiser/niche/geo with confidence band). Phase 2 endpoints (real compute): /geo-snapshot (top advertisers/niches per geo + growthVsPrior), /niche-leaderboard (rank by impressions/newCreatives/sov/spend with delta), /advertiser-timeline (per-period creatives+impressions, day/week/month granularity), /niche-pricing (proxy CPM percentiles 25/50/75), /advertiser-creatives (cursor pagination + top-regions per creative), /channel-ad-load (channel-owner perspective: total/uniqueAdvertisers/daily/estCpmRange), /cta-domains (top CTA hosts per advertiser/niche with isAffiliate/isShortener heuristics). Stubs (require async-job infra): /exports POST + /exports/<jobId> GET (sync CSV mirrors below cover typical use), /diff-webhook POST (subscribe to advertiser change events). Plan-gating: ANON 401, FREE narrow window/top, PRO/ENTERPRISE full access.
| Param | Type | Description |
|---|---|---|
(varies per endpoint) | see spec | Each endpoint has its own zod schema. Common parameters: from/to (ISO-8601 date, default last 30d / 90d), niche, geo (alpha-2 ISO), advertiser (slug). See outputs/specs/analytics-phase-2-spec.md in repo. |
curl -H "X-Api-Key: $KEY" "https://tgadsspy.com/api/v1/analytics/share-of-voice?niche=crypto&top=5"const r = await fetch('https://tgadsspy.com/api/v1/analytics/share-of-voice?niche=crypto&top=5')
const data = await r.json()
export const dynamic = 'force-dynamic'
console.log(data)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/share-of-voice?niche=crypto&top=5')
print(r.json()){
"data": { "totalImpressions": 1240000, "top": [...], "otherSov": 0.18 },
"meta": { "plan": "PRO" }
}/api/v1/analytics/advertiser-creatives.csv— Export advertiser creatives as CSV (Pro+)Same filters as JSON sibling but flat (no cursor) — up to 500 rows in one shot. 21 columns including ctaHost (for affiliate/shortener analysis), reachEstimate, spendEstimate, daysAlive, permalink. Attachment download. Pro+ only (FREE blocked).
| Param | Type | Description |
|---|---|---|
advertiser | string | Required. Advertiser slug. |
region | ISO-3166-1 alpha-2 | Filter by impressions region. |
niche | string | Filter by creative niche. |
from | ISO-8601 | Default 90 days ago. |
to | ISO-8601 | Default now. |
limit | int 1–500 | Default 500. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/advertiser-creatives.csv?advertiser=tg-mrp_tradingbot&limit=200"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/advertiser-creatives.csv?advertiser=tg-mrp_tradingbot&limit=200')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/advertiser-creatives.csv?advertiser=tg-mrp_tradingbot&limit=200')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary; see Content-Disposition header for filename)/api/v1/analytics/niche-leaderboard.csv— Export niche leaderboard as CSVTop advertisers in a niche with rank, metric, delta vs prior window, newCreatives. 8 columns. FREE: only impressions/newCreatives ranking; spend/sov ranking is Pro+.
| Param | Type | Description |
|---|---|---|
niche | string | Required. Niche slug. |
geo | ISO-3166-1 alpha-2 | Optional geo filter. |
rankBy | impressions|sov|newCreatives|spend | Default impressions. spend/sov require Pro+. |
top | int 1–50 | Default 20. FREE caps at 20. |
from | ISO-8601 | Default 30 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/niche-leaderboard.csv?niche=crypto&rankBy=impressions&top=20"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/niche-leaderboard.csv?niche=crypto&rankBy=impressions&top=20')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/niche-leaderboard.csv?niche=crypto&rankBy=impressions&top=20')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/spend-estimate.csv— Export spend estimates as CSVGroupBy advertiser/niche/geo with USD ranges and confidence band. 7 columns. FREE caps window 7d / limit 10. Estimate based on observed impressions × niche-level CPM ±25% confidence.
| Param | Type | Description |
|---|---|---|
groupBy | advertiser|niche|geo | Required. Aggregation key. |
from | ISO-8601 | Required. Window start. |
to | ISO-8601 | Required. Window end. Hard cap 365d. |
niche | string | Filter to a single niche. |
geo | ISO-3166-1 alpha-2 | Filter to a single geo. |
limit | int 1–200 | Default 50. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/spend-estimate.csv?groupBy=advertiser&niche=crypto&from=2026-04-01&to=2026-04-30"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/spend-estimate.csv?groupBy=advertiser&niche=crypto&from=2026-04-01&to=2026-04-30')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/spend-estimate.csv?groupBy=advertiser&niche=crypto&from=2026-04-01&to=2026-04-30')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/geo-snapshot.csv— Export geo snapshot as CSVLong format: top advertisers + top niches in one CSV. 5 columns (kind=advertiser|niche, rank, slug, name, impressions). Filter by 30-day default window.
| Param | Type | Description |
|---|---|---|
geo | ISO-3166-1 alpha-2 | Required. Geo code (e.g. US, BR, KZ). |
from | ISO-8601 | Default 30 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/geo-snapshot.csv?geo=KZ"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/geo-snapshot.csv?geo=KZ')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/geo-snapshot.csv?geo=KZ')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary; first half rows kind=advertiser, second half kind=niche)/api/v1/analytics/advertiser-timeline.csv— Export advertiser timeline as CSVPer-period creatives + impressions for one advertiser. 3 columns (period, creatives, impressions). Granularity day | week | month — period format ISO-date / YYYY-WW / YYYY-MM accordingly.
| Param | Type | Description |
|---|---|---|
advertiser | string | Required. Advertiser slug. |
granularity | day|week|month | Default day. |
from | ISO-8601 | Default 90 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/advertiser-timeline.csv?advertiser=tg-mrp_tradingbot&granularity=week"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/advertiser-timeline.csv?advertiser=tg-mrp_tradingbot&granularity=week')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/advertiser-timeline.csv?advertiser=tg-mrp_tradingbot&granularity=week')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/niche-pricing.csv— Export niche pricing percentiles as CSVProxy CPM percentiles 25/50/75 per niche × geo, with observed-creatives count and confidence band. 7 columns. Method = observed impressions ÷ channel-members × 1000.
| Param | Type | Description |
|---|---|---|
niche | string | Optional niche filter (omit for all). |
geo | ISO-3166-1 alpha-2 | Optional geo filter. |
top | int 1–50 | Default 20. |
from | ISO-8601 | Default 7 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/niche-pricing.csv?niche=crypto"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/niche-pricing.csv?niche=crypto')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/niche-pricing.csv?niche=crypto')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/channel-ad-load.csv— Export channel ad-load as CSVLong format: top advertisers (kind=advertiser) + daily series (kind=daily) for one channel. 4 columns (kind, key, name, creatives). Channel-owner perspective: how much ad-load you carry.
| Param | Type | Description |
|---|---|---|
channelUsername | string | Required. Channel username (no @). |
from | ISO-8601 | Default 30 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/channel-ad-load.csv?channelUsername=durov"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/channel-ad-load.csv?channelUsername=durov')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/channel-ad-load.csv?channelUsername=durov')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/cta-domains.csv— Export CTA-domain distribution as CSVTop CTA hosts per advertiser or niche, with isAffiliate / isShortener heuristic flags. 6 columns. Either advertiser OR niche is required.
| Param | Type | Description |
|---|---|---|
advertiser | string | Advertiser slug (either this or niche required). |
niche | string | Niche slug (either this or advertiser required). |
top | int 1–50 | Default 20. |
from | ISO-8601 | Default 30 days ago. |
to | ISO-8601 | Default now. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/cta-domains.csv?advertiser=tg-mrp_tradingbot"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/cta-domains.csv?advertiser=tg-mrp_tradingbot')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/cta-domains.csv?advertiser=tg-mrp_tradingbot')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/creative-fatigue.csv— Export creative-fatigue summary as CSVPer-creative summary (full daily series omitted — use JSON endpoint for that). 8 columns (creativeId, title, firstSeenAt, daysAlive, peakDay, fatigueIndex, status, permalink). Status enum: ramping | peak | fatigued | paused.
| Param | Type | Description |
|---|---|---|
advertiser | string | Filter by advertiser slug. |
niche | string | Filter by niche slug. |
creativeId | string | Single creative ID. |
from | ISO-8601 | Window start. |
to | ISO-8601 | Window end. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/creative-fatigue.csv?advertiser=tg-mrp_tradingbot"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/creative-fatigue.csv?advertiser=tg-mrp_tradingbot')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/creative-fatigue.csv?advertiser=tg-mrp_tradingbot')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)/api/v1/analytics/frequency-distribution.csv— Export frequency-distribution histogram as CSVImps/unique-channel histogram with summary stats. 3 columns (bucketLow, bucketHigh, creatives). Trailing rows: bucketLow=-1 → total, -2 → median, -3 → p90 (sentinel encoding).
| Param | Type | Description |
|---|---|---|
advertiser | string | Filter by advertiser slug. |
niche | string | Filter by niche slug. |
bins | int 5–20 | Histogram bin count. Default 10. |
from | ISO-8601 | Window start. |
to | ISO-8601 | Window end. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/frequency-distribution.csv?niche=crypto&bins=15"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/frequency-distribution.csv?niche=crypto&bins=15')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/frequency-distribution.csv?niche=crypto&bins=15')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary; first line is # comment documenting sentinels)/api/v1/analytics/competitor-overlap.csv— Export competitor-overlap matrix as CSVLong format: advertiser set sizes (kind=set) + Jaccard pair matrix (kind=pair). 8 columns (kind, a, b, jaccard, intersection, union, setSize, name). 2-5 advertisers, metric = channels | niches | creatives.
| Param | Type | Description |
|---|---|---|
advertisers | csv-list 2-5 | Required. Comma-separated advertiser slugs. |
metric | channels|niches|creatives | Default channels. |
from | ISO-8601 | Window start. |
to | ISO-8601 | Window end. |
curl -H "X-Api-Key: $KEY" -O "https://tgadsspy.com/api/v1/analytics/competitor-overlap.csv?advertisers=acme,corp,foo&metric=niches"import { writeFile } from 'node:fs/promises'
const r = await fetch('https://tgadsspy.com/api/v1/analytics/competitor-overlap.csv?advertisers=acme,corp,foo&metric=niches')
const csv = await r.text()
await writeFile('ads.csv', csv)import requests
r = requests.get('https://tgadsspy.com/api/v1/analytics/competitor-overlap.csv?advertisers=acme,corp,foo&metric=niches')
with open('ads.csv', 'wb') as f:
f.write(r.content)(text/csv binary)alert.fired (Business only).GET /api/v1/creatives/:id and related lookup endpoints.Want a feature prioritised? contact us.