From c4259c267e61cf0aca86530dec301b9a7280d1d9 Mon Sep 17 00:00:00 2001 From: zhongsongzhi-cmyk Date: Thu, 15 Oct 2026 11:57:18 +0800 Subject: [PATCH] feat(cli): explain RATE_LIMIT_BANNED_MAX and PLAN_UPGRADE_REQUIRED The OpenAPI service now returns two new rate-limit codes: - RATE_LIMIT_BANNED_MAX (429): the ban has reached its 5-minute ceiling because requests kept arriving during the cooldown. The CLI now says to stop all requests until reset_at instead of the generic retry hint. - PLAN_UPGRADE_REQUIRED (403): the route's weight exceeds the plan's bucket capacity (e.g. Free on swap / quote / follow-wallet), so it can never succeed. The CLI surfaces the upgrade message; previously 403s were printed without it. Skills and docs/cli-usage.md tell agents not to retry either code, and the stale rate=10/capacity=10 note is replaced by the plan tiers. Co-Authored-By: Claude --- docs/cli-usage.md | 7 ++++++- skills/gmgn-cooking/SKILL.md | 2 ++ skills/gmgn-market/SKILL.md | 2 ++ skills/gmgn-portfolio/SKILL.md | 2 ++ skills/gmgn-swap/SKILL.md | 2 ++ skills/gmgn-token/SKILL.md | 2 ++ skills/gmgn-track/SKILL.md | 2 ++ skills/gmgn-wallet-analysis/SKILL.md | 5 ++++- skills/gmgn-wallet-review/SKILL.md | 3 ++- skills/gmgn-wallet-score/SKILL.md | 2 ++ src/client/OpenApiClient.ts | 9 +++++++++ 11 files changed, 35 insertions(+), 3 deletions(-) diff --git a/docs/cli-usage.md b/docs/cli-usage.md index 9da0be2..09aab3d 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -937,7 +937,7 @@ Token creation is asynchronous. Poll `order get` with the returned `order_id` if All business routes are protected by GMGN's leaky-bucket limiter. Current production behavior is: -- `rate=10`, `capacity=10` +- plan-based `rate`/`capacity`: Free `5/5`, Plus `20/20`, Pro `50/50`; each route charges its own weight - every limited `429` response includes `X-RateLimit-Reset` - `X-RateLimit-Reset` is a Unix timestamp in seconds, representing when the current cooldown is expected to end @@ -951,6 +951,9 @@ Important notes: - `RATE_LIMIT_EXCEEDED` and `RATE_LIMIT_BANNED` are request-frequency limits. Continuing to send requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. - `ERROR_RATE_LIMIT_BLOCKED` is an error-count block on `POST /v1/trade/swap`. It is triggered by repeatedly hitting the same business error and should be treated as "fix the request first, then retry after reset". +- `RATE_LIMIT_BANNED_MAX` means continued requests pushed the ban to its 5-minute ceiling. Stop all requests until `reset_at`; every request sent before then resets the ban to 5 minutes. The CLI never auto-retries it. +- `PLAN_UPGRADE_REQUIRED` (HTTP 403) means the route's weight exceeds the plan's bucket capacity (for example Free calling `swap`, `multi-swap`, `order quote` or `track follow-wallet`). It can never succeed on the current plan, so it is not retried and does not trigger a ban; the CLI prints the upgrade link. +- Every rate-limited response carries `X-RateLimit-Limit` (plan capacity) and `X-RateLimit-Remaining` (budget left after this request); `RATE_LIMIT_EXCEEDED` bodies also include `remaining`, `limit` and `weight`. --- @@ -967,6 +970,8 @@ Important notes: | `AUTH_REPLAY_CHECK_UNAVAILABLE` | 503 | Anti-replay Redis unavailable (signed auth only) | | `RATE_LIMIT_EXCEEDED` | 429 | Rate limit exceeded | | `RATE_LIMIT_BANNED` | 429 | Temporarily banned due to repeated rate limit violations | +| `RATE_LIMIT_BANNED_MAX` | 429 | Ban reached the 5-minute ceiling; stop all requests until `reset_at` | +| `PLAN_UPGRADE_REQUIRED` | 403 | Endpoint unavailable on the current plan (weight exceeds plan capacity); upgrade required | | `ERROR_RATE_LIMIT_BLOCKED` | 429 | Temporarily blocked after repeated business errors on `swap` | | `TRADE_WALLET_MISMATCH` | 403 | `--from` address does not match the wallet bound to the API Key | | `CHAIN_NOT_SUPPORTED` | 400 | Unsupported chain | diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index 23aefd8..f732908 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -114,6 +114,8 @@ When a request returns `429`: - If the response body contains `reset_at` (e.g., `{"code":429,"error":"RATE_LIMIT_BANNED","message":"...","reset_at":1775184222}`), extract `reset_at` — it is the Unix timestamp when the ban lifts (typically 5 minutes). Convert to local time and tell the user exactly when they can retry. - `cooking create` is a real transaction: **never loop or auto-resubmit** after a `429`. Wait until the reset time, then ask for confirmation again before retrying. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during cooldown extend the ban by 5 seconds each time, up to 5 minutes. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). ### Credential Model diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index e9511e2..4c1df15 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -85,6 +85,8 @@ When a request returns `429`: - If the response body contains `reset_at` (e.g., `{"code":429,"error":"RATE_LIMIT_BANNED","message":"...","reset_at":1775184222}`), extract `reset_at` — it is the Unix timestamp when the ban lifts (typically 5 minutes). Convert to local time and tell the user exactly when they can retry. - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). ## `market kline` Parameters diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index 0f26bc3..a4c4876 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -82,6 +82,8 @@ When a request returns `429`: - If the response body contains `reset_at` (e.g., `{"code":429,"error":"RATE_LIMIT_BANNED","message":"...","reset_at":1775184222}`), extract `reset_at` — it is the Unix timestamp when the ban lifts (typically 5 minutes). Convert to local time and tell the user exactly when they can retry. - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). ## Usage Examples diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index c9bf962..e857dfb 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -135,6 +135,8 @@ When a request returns `429`: - `swap` is a real transaction: never loop or auto-submit repeated swap attempts after a `429`. Wait until the reset time, then ask for confirmation again before retrying. - The CLI may wait and retry once automatically for short cooldowns on read-only commands such as `order quote` and `order get`. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). - `POST /v1/trade/swap` also has an error-count limiter. Repeatedly triggering the same business error, especially `40003701` (insufficient token balance), can return `ERROR_RATE_LIMIT_BLOCKED`. When this happens, do not retry until the reset time and fix the underlying request first. ## `swap` Usage diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index e11add0..95fe25a 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -73,6 +73,8 @@ When a request returns `429`: - If the response body contains `reset_at` (e.g., `{"code":429,"error":"RATE_LIMIT_BANNED","message":"...","reset_at":1775184222}`), extract `reset_at` — it is the Unix timestamp when the ban lifts (typically 5 minutes). Convert to local time and tell the user exactly when they can retry. - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). ## Parameters — `token info` / `token security` / `token pool` diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 39d88f1..9c8cf22 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -87,6 +87,8 @@ When a request returns `429`: - If the response body contains `reset_at` (e.g., `{"code":429,"error":"RATE_LIMIT_BANNED","message":"...","reset_at":1775184222}`), extract `reset_at` — it is the Unix timestamp when the ban lifts (typically 5 minutes). Convert to local time and tell the user exactly when they can retry. - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). ## Usage Examples diff --git a/skills/gmgn-wallet-analysis/SKILL.md b/skills/gmgn-wallet-analysis/SKILL.md index 770ff3c..5238c48 100644 --- a/skills/gmgn-wallet-analysis/SKILL.md +++ b/skills/gmgn-wallet-analysis/SKILL.md @@ -374,7 +374,10 @@ live-positions section is missing rather than letting its absence read as "no po **On `429`:** stop. Read `X-RateLimit-Reset` or `reset_at`, convert to the user's local time and state it: *"Rate-limited — retry this wallet after 14:32:05 (~4 minutes)."* Report whatever tiers succeeded rather than discarding the run. Repeated requests during a cooldown extend the ban by -5 seconds each, up to 5 minutes — never loop retries. +5 seconds each, up to 5 minutes — never loop retries. `RATE_LIMIT_BANNED_MAX` means the ban already +hit that 5-minute ceiling: stop every remaining call, not just the failing tier. `403 +PLAN_UPGRADE_REQUIRED` means the endpoint is unavailable on the user's plan — don't retry; record +the gap and show the response's `upgrade_message`. **The 1d window is rolling.** A `profits --period 1d` pull minutes after a screenshot reads 15–20% lower because profitable trades roll out of the window. 7d/30d are stable — which is why the diff --git a/skills/gmgn-wallet-review/SKILL.md b/skills/gmgn-wallet-review/SKILL.md index aa8818a..ca6a3be 100644 --- a/skills/gmgn-wallet-review/SKILL.md +++ b/skills/gmgn-wallet-review/SKILL.md @@ -113,7 +113,8 @@ Rules for pulling: an error message mentioning `429` or `RATE_LIMIT`. It never arrives as a normal JSON body. Don't search the returned data for "429": prices, amounts and timestamps contain it all the time. - **Rate limits: one `429` / `RATE_LIMIT` stops everything.** Make no further calls and don't - retry; retries extend a `RATE_LIMIT_BANNED` ban. If calls 1–4 succeeded, continue without 5–6 + retry; retries extend a `RATE_LIMIT_BANNED` ban (`RATE_LIMIT_BANNED_MAX` means it is already at + the 5-minute ceiling). If calls 1–4 succeeded, continue without 5–6 and say those parts were skipped. If 1–4 did not finish, tell the user to wait for the reset time and stop. - **A rejected key stops everything too.** `config --check` only confirms a key is present, not diff --git a/skills/gmgn-wallet-score/SKILL.md b/skills/gmgn-wallet-score/SKILL.md index 2f980df..33c6158 100644 --- a/skills/gmgn-wallet-score/SKILL.md +++ b/skills/gmgn-wallet-score/SKILL.md @@ -544,6 +544,8 @@ A single run of this skill's script can burn through several of these in sequenc - Convert whichever timestamp you got to the user's local time and state it plainly, e.g. *"Rate-limited — you can retry this wallet after 14:32:05 (in ~4 minutes)."* Do this even if the run partially succeeded (see below) — the user needs to know when the rest of the analysis can resume. - **Resume, don't restart**: if the script is mid-run (e.g. the Dev security scan hits `429` after `portfolio stats` and `activity` already succeeded), report what you already have (stats/tags/track-record score can still be shown), state the reset time for the remaining calls, and re-run only those remaining calls after it passes — don't re-fetch data you already have. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown extend the ban by 5 seconds each time, up to 5 minutes. Never loop retries — wait for the stated reset time before trying again. +- On `RATE_LIMIT_BANNED_MAX`, the ban has hit the 5-minute ceiling because requests kept arriving during the cooldown. Stop **all** GMGN calls for this task (not just the failing one) and tell the user the exact reset time; any request before `reset_at` resets the ban to 5 minutes again. +- On `403 PLAN_UPGRADE_REQUIRED`, the endpoint costs more rate-limit weight than the user's plan capacity, so it can never succeed on this plan — do not retry or wait. Tell the user the endpoint is unavailable on their current plan and show the `upgrade_message` from the response (it contains the upgrade link `https://gmgn.ai/ai?chain=bsc&tab=paid_plans`). - Scoring multiple wallets in one request (a leaderboard-style comparison): space the calls out or reduce `--sample` rather than firing all wallets' full analysis concurrently. If one wallet in the batch gets rate-limited, report the completed wallets immediately and tell the user when the rest will be ready, rather than holding the whole batch back silently. ## Notes diff --git a/src/client/OpenApiClient.ts b/src/client/OpenApiClient.ts index 9565001..3b1d0ee 100644 --- a/src/client/OpenApiClient.ts +++ b/src/client/OpenApiClient.ts @@ -787,6 +787,11 @@ function buildOpenApiErrorMessage(params: OpenApiErrorParams): string { let message = parts.join(" "); + if (params.apiError === "PLAN_UPGRADE_REQUIRED") { + const upgrade = params.upgradeMessage ?? params.upgradeUrl ?? "Upgrade your plan to use this endpoint."; + return `${message}. This endpoint is unavailable on your current plan; retrying will not help. ${upgrade}`; + } + if (params.status !== 429) { return message; } @@ -799,6 +804,10 @@ function buildOpenApiErrorMessage(params: OpenApiErrorParams): string { return `${message}. Repeated business errors triggered a temporary block until ${resetText}. Fix the underlying request before retrying.`; } + if (params.apiError === "RATE_LIMIT_BANNED_MAX") { + return `${message}. The ban has reached the 5-minute maximum. Stop ALL requests until ${resetText}; every request sent before then resets the ban to 5 minutes.`; + } + if (params.apiError === "RATE_LIMIT_EXCEEDED" || params.apiError === "RATE_LIMIT_BANNED") { const retryGuidance = `Rate limit resets at ${resetText}. Stop sending requests before then; repeated requests can extend the ban by 5s up to 5 minutes.`; if (params.apiError === "RATE_LIMIT_EXCEEDED" && params.upgradeMessage) {