# Exchange Listing Monitor - Binance, Upbit, OKX, Bybit Feed (`datadeltas/exchange-listing-monitor`) Actor

Change feed for new coin listings and delistings across Binance, Upbit, Bithumb, OKX, Bybit, KuCoin and Kraken. Keeps state between runs and emits only NEW announcements with extracted symbols and event type. Schedule every 5-15 minutes; pay only per listing detected.

- **URL**: https://apify.com/datadeltas/exchange-listing-monitor.md
- **Developed by:** [Daniel Matthee](https://apify.com/datadeltas) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $14.00 / 1,000 listing announcement detecteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Exchange Listing Monitor — new coin listings & delistings from 7 CEXs

**New coin listing and delisting announcements from Binance, Upbit, Bithumb,
OKX, Bybit, KuCoin and Kraken, in one deduped feed.** Each scheduled run emits
only the announcements it has never seen before, already classified as a
`listing` or a `delisting`, with the tickers pulled out of the title.

Keyless official venue announcement APIs — no exchange account, no API key, no
Playwright.

### Read this before you buy: latency

This is a **scheduled poller**, not a websocket. Freshness equals your schedule
interval: on a 5-minute schedule an announcement reaches you within roughly 0-5
minutes of the venue publishing it. That is fast enough to be first into a
research pipeline, a Slack/Telegram alert, a watchlist, or a slower systematic
strategy. It is **not** co-located front-running infrastructure, and nothing in
this listing should be read as promising sub-second delivery. None of these
seven venues publish a keyless push channel for announcements, so no polling
actor can honestly claim otherwise.

### Example output

One real record, exactly as pushed to the dataset:

```json
{
  "venue": "okx",
  "announcementId": "okx-to-list-spkusd-x-perp",
  "title": "OKX to list SPKUSD X-Perp",
  "eventType": "listing",
  "symbols": ["SPK"],
  "publishedAt": "2026-08-28T03:00:09Z",
  "url": "https://www.okx.com/help/okx-to-list-spkusd-x-perp",
  "venueCategory": "announcements-new-listings",
  "detectedAt": "2026-08-31T12:07:00Z"
}
```

A Korean venue record — the title stays verbatim, and `eventType` plus
`symbols` make it usable without translating anything:

```json
{
  "venue": "bithumb",
  "announcementId": "1654694",
  "title": "인터폴드(FOLD), 이유알코인(EURC) 원화 마켓 추가",
  "eventType": "listing",
  "symbols": ["FOLD", "EURC"],
  "publishedAt": "2026-08-28T02:11:27Z",
  "url": "https://feed.bithumb.com/notice/1654694",
  "venueCategory": "마켓 추가",
  "detectedAt": "2026-08-31T12:07:00Z"
}
```

#### Fields

| Field | What it is |
|---|---|
| `venue` | `binance` / `upbit` / `bithumb` / `okx` / `bybit` / `kucoin` / `kraken` |
| `announcementId` | Stable id unique within the venue — the dedupe key |
| `title` | The venue's headline, verbatim and untranslated |
| `eventType` | `listing`, `delisting` or `unknown` (see below) |
| `symbols` | Tickers named by the title, upper-cased, quote suffix stripped (`ACXUSDT` → `ACX`). `[]` when nothing is nameable |
| `publishedAt` | When the venue published it, ISO-8601 **UTC** (Korean venues converted from KST) |
| `url` | Link to the announcement on the venue's own site |
| `venueCategory` | The venue's own channel label, verbatim — filter looser than we do |
| `detectedAt` | When the monitor first saw it |

### Venues covered

| Venue | Region | Channel |
|---|---|---|
| Binance | global | "New Cryptocurrency Listing" + "Delisting" announcement catalogs |
| Upbit | KR | Trading notices — the biggest listing-pump venue |
| Bithumb | KR | Public notices |
| OKX | global | New listings + delistings announcements |
| Bybit | global | New listings + delistings announcements |
| KuCoin | global | New listings + delistings announcements |
| Kraken | global | Blog "Asset Listings" posts |

One venue being temporarily unreachable degrades to a warning in the log and
the other six still emit. Only if **every** selected venue fails does the run
fail — a dead run must never look like a quiet market.

### Input options

| Input | Default | What it does |
|---|---|---|
| `venues` | all seven | Which announcement channels to poll |
| `eventTypes` | all | Filter to `listing`, `delisting` and/or `unknown` |
| `symbols` | all | Ticker watchlist, e.g. `["PEPE","WIF","ARB"]` |
| `sinceHours` | `48` | First-run backfill window in **hours**; later runs emit only unseen announcements |
| `maxItems` | `300` | Cap per run, **split fairly across the venues that have announcements** so a busy venue cannot crowd out the rest. Records over the cap are not marked seen, so they emit next run — nothing is lost |
| `userAgent` | browser default | Rarely needed; several venues 403 bare non-browser clients |

### How `eventType` is decided — and why it is cautious

`eventType` comes from the announcement **title**, never from the channel it
arrived on, because those channels are noisy in ways that would cost you money:

- Bybit's "New Listings" channel carries prize-pool promotions.
- Binance's "Delisting" catalog carries routine margin-pair housekeeping.
- Upbit's caution designation (`거래 유의 종목 지정` / "Investment Warning")
  frequently *precedes* a delisting without being one.
- OKX's delistings channel carries token-migration notices.

Anything the title does not clearly say is `unknown` — and the venue's own
label survives in `venueCategory`, so if you want a looser rule you can build
it from the raw data rather than un-pick ours.

`symbols` is cautious the same way: parenthesised tickers win, ALLCAPS tokens
are taken only from titles already classified as a listing or delisting, and
titles that name no ticker (`"Bybit to Delist 1 Trading Pair(s)"`,
`"KuCoin Will Delist Certain Projects"`) or name it in Chinese characters
return `[]` rather than a guess. An empty array falls back to the title; a
wrong ticker fires a wrong trade.

### Use cases

**"Alert me the moment a coin I hold gets a delisting notice."**
Schedule every 5-15 minutes with `eventTypes: ["delisting"]` and your holdings
in `symbols`. Wire the dataset to Slack, Telegram or Discord via an Apify
integration. Delisting notices move price hard and the venues do not email you.

**"Feed my listing-arb / momentum bot every new CEX listing."**
Default inputs, `eventTypes: ["listing"]`, schedule every 5 minutes. Each
dataset item is a fresh, deduped event with the ticker already extracted, so
your bot consumes rows instead of scraping seven announcement pages and writing
seven parsers.

**"Track which venues are listing our token, and our competitors'."**
Put your ticker and your comparables in `symbols` and leave `eventTypes` empty.
You get a dated, sourced record of every venue announcement naming them —
useful for BD reporting, exchange-relations decks, and spotting a competitor's
Upbit or Binance listing the day it lands.

### Update & maintenance promise

Venue payload shapes are checked weekly and the parsers are covered by 125
offline tests running against trimmed real captures from all seven venues.
Schema drift logs a clear named warning instead of crashing a run, and a venue
whose API changes degrades to a warning while the rest keep emitting.

### Known limits (stated up front)

- **Latency is your schedule interval**, not milliseconds. See above.
- **Kraken** publishes 10 blog posts per feed page with no paging, and
  **Bithumb**'s API caps at 20 notices with no paging — roughly one week each.
  A very large `sinceHours` cannot invent history those two venues do not
  publish. Binance, Upbit, OKX, Bybit and KuCoin page back much further.
- **Korean titles ship untranslated.** Upbit renders most notices in English;
  Bithumb and some Upbit notices stay Korean. `eventType` and `symbols` are the
  machine-readable layer over both.
- **Coinbase and Gate.io are not in v1.** Coinbase publishes no keyless
  announcement feed (its blog is a JavaScript app and the old RSS paths
  redirect), and Gate.io's announcement endpoint is edge-blocked. Both are
  tracked for v1.1.
- Some announcement pages are bot-protected against non-browser clients
  (Binance returns an empty 202, Bithumb's feed host sits behind Cloudflare).
  They open normally in a browser. The monitor never fetches them — it only
  emits the link.

### Pricing (pay-per-event)

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Venue polled | $0.001 per venue successfully polled (a failed venue is not charged) |
| Listing announcement detected | $0.02 per new announcement record |

A steady-state 5-minute schedule usually emits nothing and costs $0.012 a run.
You pay for events, not for polling noise.

### Changelog

- **0.1.1** — `maxItems` is now split fairly across the venues that returned
  announcements instead of being consumed first-come-first-served. With a small
  cap the first venue polled could take the whole allowance and the other six
  emitted nothing in that run; now every contributing venue gets its share and
  the unused share of a quiet venue is handed back to the busy ones. No change
  to record fields, inputs, state or pricing.
- **0.1** — first release. Seven venues (Binance, Upbit, Bithumb, OKX, Bybit,
  KuCoin, Kraken), conservative title-based `listing`/`delisting`
  classification, ticker extraction, hour-precision backfill window,
  per-venue failure degradation.

# Actor input Schema

## `venues` (type: `array`):

Which exchange announcement channels to poll. Default: all seven. Every venue is a keyless official announcement API. A venue that is temporarily unreachable degrades to a warning - the others still emit. Only if ALL selected venues fail does the run fail.

## `eventTypes` (type: `array`):

Optional filter on the classified event type. 'listing' = the venue is adding a coin, pair or contract; 'delisting' = the venue is removing one; 'unknown' = the announcement arrived on a listing channel but its title does not clearly say either (promotions, deposit/withdrawal pauses, Upbit caution designations). Classification is deliberately conservative - see the README. Leave empty for all types.

## `symbols` (type: `array`):

Optional filter, e.g. \["PEPE", "WIF", "ARB"]. An announcement is emitted when any listed ticker was extracted from its title, or - when extraction was too cautious to label it - when the ticker appears in the title text. Case-insensitive. Leave empty to monitor every announcement.

## `sinceHours` (type: `integer`):

The first run emits announcements published within this many HOURS; later runs emit only announcements never seen before. Hours, not days, because listing latency is measured in minutes. NOTE: Kraken's feed carries 10 posts and Bithumb's API caps at 20 notices with no paging (~1 week each), so a very large window is bounded by what those venues publish. Binance, Upbit, OKX, Bybit and KuCoin page back much further.

## `maxItems` (type: `integer`):

Hard cap on announcement records emitted in one run (keeps test runs cheap). Records beyond the cap are NOT marked as seen, so they emit on the next run - nothing is lost.

## `userAgent` (type: `string`):

Override the browser User-Agent used for venue requests. Several venues return 403 or 429 to bare non-browser clients, so leave this empty unless you have a specific reason to change it.

## Actor input object example

```json
{
  "venues": [
    "binance",
    "upbit",
    "bithumb",
    "okx",
    "bybit",
    "kucoin",
    "kraken"
  ],
  "eventTypes": [],
  "symbols": [],
  "sinceHours": 48,
  "maxItems": 300
}
```

# Actor output Schema

## `records` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "eventTypes": [],
    "symbols": [],
    "userAgent": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("datadeltas/exchange-listing-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "eventTypes": [],
    "symbols": [],
    "userAgent": "",
}

# Run the Actor and wait for it to finish
run = client.actor("datadeltas/exchange-listing-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "eventTypes": [],
  "symbols": [],
  "userAgent": ""
}' |
apify call datadeltas/exchange-listing-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datadeltas/exchange-listing-monitor"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/nb7qxzAJ0vE9k8HVQ/builds/N0w5W21Pf4OA4lv2n/openapi.json
