GMGN Callout OpenAPI
发布时间:2026-09-07 | 浏览:1
The GMGN Callout OpenAPI lets approved partners programmatically publish and read on-chain "callouts" — a signal from a wallet that it is calling a specific token, optionally with a thesis and a linked Twitter/X identity.
Application Form: https://docs.google.com/forms/d/e/1FAIpQLScRnqI0PSQBEewJnF3JnfRSm50PylZRcIAfE7bl96Yp7RCQCg/viewform?usp=dialog
All endpoints are POST , accept and return application/json (UTF-8), and are authenticated with an AK/SK HMAC signature on every request. Rate limits are enforced per ak . Your ak / sk credentials are issued by GMGN.
https://papi.gmgn.ai/callout/openapi/v1
application/json (UTF-8)
AK/SK HMAC-SHA256 signature (headers on every request)
Every callout across all chains and wallets, newest first. The simplest way to integrate — no filters, no parameters beyond a cursor.
One token's cross-wallet feed, plus its Best Callout and paid pinned declaration.
One wallet's callout history, and the only endpoint that reports the true callout source .
Publish a callout, or a Callback onto someone else's call.
If you only want to consume the firehose — a community bot, a feed, a dashboard — you need /global and nothing else .
POST /global — Global callout stream (read-only)
The global callout stream: every callout across all chains and all wallets , in strict reverse-chronological order (newest first), with a cursor that pages toward older records. Built for read-only community, bot, and feed integrations.
Four fixed constraints
These are fixed server-side and cannot be changed by any request parameter :
Last 7 days only
Once paging reaches the 7-day boundary, next_page_token stops being returned and pagination terminates naturally.
Per ak , counted independently from the other endpoints' shared ~10 QPS pool. See Rate limits.
The server ignores any chains / only_kol / only_declaration / limit you send. It always returns the full cross-chain stream.
100 records per page
Fixed page size.
Pagination cursor ( next_page_token from the previous page). Omit or leave empty for the first page.
ℹ️ Every field other than page_token is ignored — the global stream accepts no filters. An empty body or a malformed body is treated as a first-page request and returns 200 , not 400 .
Success response
records[] fields
Wallet that made the callout.
Token address called.
Token price at callout time.
Market cap at callout time.
Callout unique identifier.
Callout time, Unix milliseconds .
Token logo URL.
Current multiplier vs. callout price.
twitter_username
Caller display name.
Caller @handle .
holding_percentage
Caller's holding percentage of the token.
Callout source — GMGN-native callouts report the device ( web / android / ios ); third-party platform callouts report that platform's identifier.
Whether the calling wallet is a GMGN-tagged KOL wallet (KOLs who linked their own Twitter/X; public data). Always returned as true or false .
Whether this is a paid declaration callout. omitempty — present only when true ; a missing field means false .
Ordering and pagination
Records are in strict descending order by create_time (then ulid ), merged across chains — paging never duplicates or skips a record.
Records are in strict descending order by create_time (then ulid ), merged across chains — paging never duplicates or skips a record.
Pass page_token = the previous response's next_page_token to fetch the next (older) page.
Pass page_token = the previous response's next_page_token to fetch the next (older) page.
An empty or absent next_page_token means you have reached the 7-day boundary or exhausted the data — stop paging.
An empty or absent next_page_token means you have reached the 7-day boundary or exhausted the data — stop paging.
Other omitempty fields (e.g. callback_* ) are absent when they have no value.
Other omitempty fields (e.g. callback_* ) are absent when they have no value.
ℹ️ This endpoint returns only the boolean is_declaration flag. It does not return paid-declaration details such as amount_usd or the declaration's start time. Contact us if you need those.
Response envelope
Every endpoint returns a {code, message, data} envelope. Always unwrap data — do not read top-level fields as business data.
HTTP 200 + code = 0 — Request succeeded. The business payload is under data .
HTTP 200 + code = 0 — Request succeeded. The business payload is under data .
HTTP 4xx / 5xx — code (int) and message (string) describe the error. No data is returned.
HTTP 4xx / 5xx — code (int) and message (string) describe the error. No data is returned.
Two layers of outcome for writes
For POST /create , a transport-level HTTP 200 does not mean the callout was accepted. There are two distinct layers:
Transport / auth / limits — HTTP status code ( 400 , 401 , 429 , 500 ).
Transport / auth / limits — HTTP status code ( 400 , 401 , 429 , 500 ).
Business rule — inside data , the boolean data.success . When false , inspect data.reject.reason .
Business rule — inside data , the boolean data.success . When false , inspect data.reject.reason .
Prices, market caps, multipliers, percentages, and paid amounts are strings (e.g. "0.5" , "3.2" ) to preserve precision. Parse with a decimal-safe library, not native floats.
Prices, market caps, multipliers, percentages, and paid amounts are strings (e.g. "0.5" , "3.2" ) to preserve precision. Parse with a decimal-safe library, not native floats.
Timestamps are Unix milliseconds (int64) unless a field is explicitly an ISO-8601 string (e.g. created_at on the /token feed).
Timestamps are Unix milliseconds (int64) unless a field is explicitly an ISO-8601 string (e.g. created_at on the /token feed).
ulid is the unique identifier of a callout.
ulid is the unique identifier of a callout.
Fields marked omitempty are absent from the JSON when empty — do not assume a key exists.
Fields marked omitempty are absent from the JSON when empty — do not assume a key exists.
Every request must be signed with your AK/SK credentials using HMAC-SHA256 . Keep the sk server-side — never ship it in a client app or browser.
Required headers
Your issued access key.
Current time as a Unix millisecond timestamp.
Lowercase hex HMAC-SHA256 of the payload below.
Signature payload
Concatenate these fields in order, with no separators :
Same value as X-Ak .
Same value as X-Timestamp (millisecond string).
Uppercase HTTP method, e.g. POST .
Request path including the API prefix, excluding host and query , e.g. /callout/openapi/v1/global .
URL query string (empty string for these POST endpoints).
Raw request body — must match the bytes actually sent exactly. Empty string if there is no body.
⚠️ Timestamp window: ±30 seconds. If X-Timestamp differs from server time by more than 30s the request is rejected. Keep your client clock synced (NTP).
ℹ️ Because the signature covers the raw body bytes , sign the exact serialized JSON string you send. Do not re-serialize, pretty-print, or reorder keys between signing and sending.
Signing examples
Authentication failures (HTTP 401)
missing auth header
One of X-Ak / X-Timestamp / X-Signature is absent.
invalid timestamp
X-Timestamp is malformed or outside the ±30s window.
The access key is not recognized.
invalid signature
The computed signature does not match.
api not allowed
This ak is not permitted to call this endpoint.
The request originated from a non-allowlisted IP.
Limits are enforced per ak .
Request rate (QPS)
Per your allocation (default ≈ 10 QPS)
HTTP 429 rate limit exceeded
/create , /get_record , /token
Global stream rate (QPS)
1 QPS (strict, per ak )
HTTP 429 rate limit exceeded
Callout 24h quota
Per your allocation
HTTP 429 ak daily quota exceeded
The read endpoints ( /global , /get_record , /token ) do not consume the callout 24h quota.
The read endpoints ( /global , /get_record , /token ) do not consume the callout 24h quota.
/global has its own 1 QPS bucket , counted separately from the ~10 QPS pool shared by the other endpoints. It is also restricted to the last 7 days and supports no filtering — see the four fixed constraints.
/global has its own 1 QPS bucket , counted separately from the ~10 QPS pool shared by the other endpoints. It is also restricted to the last 7 days and supports no filtering — see the four fixed constraints.
The 24h callout quota is distinct from the per- twitter_id and per-wallet business limits, which surface as business rejects ( data.success = false ) rather than HTTP 429 — see reject reasons.
The 24h callout quota is distinct from the per- twitter_id and per-wallet business limits, which surface as business rejects ( data.success = false ) rather than HTTP 429 — see reject reasons.
POST /token — Callouts by token
Return the cross-wallet callout feed for one token: all callouts on that token, the highest-multiplier callout ( top_message ), the paid pinned declaration ( vip_message ), and a pagination cursor.
Pagination cursor. Empty for the first page.
Page size, max 50 . Values <= 0 or > 50 are capped at 50 .
⚠️ Note the asymmetry with /get_record : this endpoint caps an out-of-range limit at 50, whereas /get_record falls back to 20 .
Success response
messages[] fields
Message identifier.
Callout thesis / text (source language).
display_content
Translated text. Always empty on this API — the OpenAPI path forces an empty app_lang , so only the source content is returned.
Attached media, if any.
Caller display name.
profile_image_url
Caller avatar URL.
Caller wallet address.
user_twitter_url
Caller Twitter/X profile URL.
Caller follower count.
Like count. Currently always 0 (retained for structural compatibility after the pump sunset).
Reply count. Currently always 0 (same reason).
ISO-8601 timestamp of the callout.
Always gmgn on this endpoint — see note below.
Multiplier vs. callout price.
Callout unique identifier.
How many times this post has been called back.
callback_to_ulid
ULID of the original post this one calls back to.
callback_to_chain
Chain of that original post.
callback_to_wallet
Wallet of that original post.
callback_count is present on both original posts and callback posts — it counts how many callbacks a post received. The callback_to_* trio points at the original post being called back; when all three are empty, the record is an original post, not a callback . All are omitempty .
top_message — Best Callout
The highest- multiplier callout for this token in the last 30 days (compared as decimals).
⚠️ Returned on every page , not just the first — the current implementation does not distinguish first page from subsequent pages. It is absent when there are no callouts in the 30-day window.
vip_message — paid pinned declaration
The paid pinned declaration, assembled from callout-declaration records with success recharge status. Same structure as a messages[] item, plus:
The paid tier amount. Filled only on vip_message — never on top_message .
omitempty — absent when there is no success record.
omitempty — absent when there is no success record.
Sits alongside top_message ; both can appear at the same time .
Sits alongside top_message ; both can appear at the same time .
ℹ️ vip_message.amount_usd is paid-tier information and is passed through to partners as-is by this endpoint.
declaration_level
The token's current recharge tier. This is a top-level field describing the token , not a property of any single declaration. In phase one it is the default value "199" , reserved for future tier expansion and bidding floor prices.
has_more (bool) is the authoritative signal for whether to keep paging.
has_more (bool) is the authoritative signal for whether to keep paging.
next_cursor is returned only when has_more is true . When there are no more pages the field is omitted entirely (not an empty string or null ).
next_cursor is returned only when has_more is true . When there are no more pages the field is omitted entirely (not an empty string or null ).
⚠️ Page on has_more , not on the presence of next_cursor .
ℹ️ On /token , source is always gmgn — this aggregated token-page view does not pass through third-party source identifiers. To distinguish callouts by originating platform, use /get_record .
POST /get_record — Callouts by wallet
Return the callout history of a single wallet, newest first, with a cursor that pages toward older records.
Wallet address to query.
Page size. Must be between 1 and 50.
Pagination cursor ( next_page_token from the previous page). Empty for the first page.
⚠️ limit out of range falls back to the default 20, not to 50. Sending limit: 100 returns 20 records, not 50. The same is true for limit <= 0 . To get 50 per page you must send exactly 50 . (This differs from /token , which caps at 50.)
Success response
records[] fields
Wallet that made the callout.
Token address called.
Token price at callout time.
Market cap at callout time.
Callout unique identifier.
Current multiplier vs. callout price.
Token logo URL.
twitter_username
Caller display name.
holding_percentage
Caller's holding percentage of the token.
True callout source — see below.
How many times this post has been called back.
callback_to_ulid / callback_to_chain / callback_to_wallet
The original post this one calls back to. omitempty .
source — the only endpoint that reports the real platform
source reports the true origin of the callout: GMGN-native callouts report the device name ( web / android / ios ), and third-party platform callouts report that platform's identifier.
ℹ️ This endpoint resolves source per call_wallet , so you can use it to distinguish platforms. /token always reports gmgn and does not pass through third-party identifiers.
Same semantics as /token — see Callbacks. All callback_* fields are omitempty .
Pass page_token = the previous response's next_page_token to fetch the next (older) page. An empty next_page_token means there are no older records.
POST /create — Create a callout
Publish a callout: one on-chain wallet calling one token. The source platform is derived automatically from your ak — you do not pass it.
Chain, e.g. sol / eth / bsc .
On-chain wallet address making the callout.
Token address being called.
Caller's Twitter/X rest_id . Numeric-only string ( ^[0-9]+$ ).
@handle , used as a display fallback.
twitter_username
Display name, used as a display fallback.
Callout rationale text. Subject to content moderation.
callback_to_chain
Callback: chain of the original post being called back.
callback_to_wallet
Callback: wallet of the original post.
callback_to_ulid
Callback: ULID of the original post.
ℹ️ The three callback_to_* fields describe a Callback (calling in response to another callout). Supply all three together, or none.
Success response (HTTP 200)
ulid is the unique identifier of the callout.
Business reject (HTTP 200)
The request was well-formed and authenticated, but a business rule blocked it:
⚠️ Branch on reason (stable semantic string), never on code (troubleshooting aid only).
holding_too_low
The wallet's holding of this token is below the threshold.
Same wallet + same token is in a cooldown window. next_available_at (ms) and interval_minutes describe it.
24h callout count exceeded. next_available_at is the window reset time.
twitter_daily_limit
This twitter_id exceeded its 24h callout count. next_available_at is the window reset time.
content_violation
call_thesis hit the content-moderation filter.
content_audit_unavailable
Moderation service unavailable — fail-closed reject. Retry later.
twitter_not_bound
The calling wallet has no bound Twitter/X account. Not triggered today — see below.
twitter_not_verified
The bound Twitter/X account has not passed verification. Not triggered today — see below.
Semantic reject reason (use this).
Numeric business code (troubleshooting only).
next_available_at
For time-based rejects ( cooldown , daily_limit , twitter_daily_limit ): next allowed time, Unix ms.
interval_minutes
For cooldown : length of the cooldown window in minutes.
ℹ️ twitter_not_bound / twitter_not_verified will not fire under the current external-platform policy. External platforms run with require_twitter_bind=false and require_twitter_verified=false , so callouts are not checked against GMGN's internal Twitter-binding records — that role is served by the required twitter_id plus its rest_id format constraint. These two codes are reserved for future use; you do not need handler branches for them yet.
ℹ️ Token safety checks (honeypot, etc.) are currently disabled. They were previously a hard block, but new pools lacking safety data caused false rejections, so the hard block was removed in favor of a disclaimer approach. No safety-related reason is returned today. This may be reinstated later with separate notice.
Invalid parameters (HTTP 400)
Missing or non-numeric twitter_id returns the above; other missing required fields return a corresponding 400 .
HTTP status codes
Request handled. For writes, inspect data.success .
Invalid parameters (missing/malformed required field). /global never returns this — a malformed body is treated as the first page.
Signature authentication failed. See Authentication failures.
Rate limit or 24h quota exceeded. See Rate limits.
Internal error. Safe to retry with backoff.
OpenAPI 3.0 specification
The complete OAS 3.0 definition below can be imported directly into Swagger UI, Redoc, or Postman.
ℹ️ The X-Signature value must be computed per request — it cannot be a static value in Swagger/Postman. Use a pre-request script to build the signature over ak + timestamp + method + path + rawQuery + body .
Last updated 16 days ago
POST /global — Global callout stream (read-only)
Response envelope
POST /token — Callouts by token
POST /get_record — Callouts by wallet
POST /create — Create a callout
HTTP status codes
OpenAPI 3.0 specification