Tok{In} API
The Tok{In} API offers an array of features that can help determine whether a token is malicious or not. From storage checks, to simulation and static analysis features. The Tok{In} API supports all ERC20 contracts on Ethereum, Binance Smart Chain, Arbitrum, Base, Avalanche, and Robinhood Chain (New). The features cover all Uniswap V2/V3/V4 forks, Aerodrome V2/Slipstream (Base), and launchpad tokens — both bonding curves (four.meme and flap.sh on BSC, trench.today and flap on Robinhood Chain) and launchpads that seed a standard DEX pool directly (Pons, Noxa, Virtuals, Doppler, varo.rialto, bow.fun and letscash.fun on Robinhood Chain).
For AI coding agents (Claude Code, Cursor, Copilot, etc.): a migration guide written specifically for you lives at
AGENTS.mdalongside this page (canonical URL:https://docs.dedaub.com/docs/token_safety/AGENTS). Read it end-to-end before changing any code that integrates with Tok{In} — it covers the GoPlus → Tok{In} drop-in swap, field mappings, always-nullfields, and common migration pitfalls.
Usage
curl -X 'GET' \
'https://tokin-api.dedaub.com/token/{chain}/{token_address}' \
-H 'accept: application/json' \
-H 'X-API-Key: YOUR-API-KEY'
chain: The chain of the token
token_address: The address of the token
User Interface
A Swagger UI for easier interaction with the API is available at https://tokin-api.dedaub.com .
Available Chains
| Chain | Slug | Chain ID |
|---|---|---|
| Ethereum | ethereum | 1 |
| Binance Smart Chain | binance | 56 |
| Arbitrum | arbitrum | 42161 |
| Base | base | 8453 |
| Avalanche | avalanche | 43114 |
| Robinhood Chain (New) | robinhood | 4663 |
DEX coverage is most complete on Ethereum, with more pools being added to the other chains over time.
Quote tokens per chain
Pool discovery and liquidity valuation are anchored on each chain's wrapped native token plus its canonical ~$1 stablecoins. That anchor set is not the same on every chain:
| Chain | Wrapped native | Stable anchors |
|---|---|---|
| Ethereum, Arbitrum, Base | WETH | USDC, USDT |
| Binance Smart Chain | WBNB | USDC, USDT |
| Avalanche | WAVAX | USDC, USDT |
| Robinhood Chain | WETH | USDG, USDE |
Robinhood Chain has no canonical USDC or USDT — USDG and USDE are the real ~$1 tokens there, and any USDC/USDT-named deployment on 4663 is an impostor. Pools quoted in a non-anchor token can still be valued when a price is available (see Notes on liquidity).
Response Formats
The Tok{In} API supports multiple response formats via the response_format query parameter.
Available Formats
| Format | Description |
|---|---|
dedaub | Default format |
goplus | GoPlus-compatible format |
Usage
# Default (Dedaub format)
GET /token/{chain}/{token_address}
# GoPlus format
GET /token/{chain}/{token_address}?response_format=goplus
GoPlus Format Mapping
The GoPlus format transforms the default Tok{In} schema to a GoPlus-compatible one, with the following caveats.
Direct Mappings
| Dedaub Field | GoPlus Field | Notes |
|---|---|---|
is_open_source | is_open_source | Boolean → "1"/"0" string |
is_proxy | is_proxy | Boolean → "1"/"0" string |
mint_or_burn_function | is_mintable | Boolean → "1"/"0" string |
owner_address | owner_address | Direct string |
can_selfdestruct | selfdestruct | Boolean → "1"/"0" string |
external_call | external_call | Boolean → "1"/"0" string |
is_in_dex | is_in_dex | Boolean → "1"/"0" string |
receive_tax | buy_tax | Decimal as string — pool-aware, see caveat 4 |
send_tax | sell_tax | Decimal as string — pool-aware, see caveat 4 |
cannot_buy | cannot_buy | Boolean → "1"/"0" string |
tax_can_be_modified | slippage_modifiable | Boolean → "1"/"0" string |
trading_cooldown | trading_cooldown | Boolean → "1"/"0" string |
creator_address | creator_address | Direct string |
creator_percent | creator_percent | Decimal as string |
is_launchpad_token | launchpad_token | Boolean → "1"/"0" string |
Computed Mappings
| GoPlus Field | Derived From | Logic |
|---|---|---|
transfer_pausable | cannot_buy, pause_status_can_be_modified | True if either is True |
is_blacklisted | has_blacklist_or_whitelist | Same value (temporary) |
is_whitelisted | has_blacklist_or_whitelist | Same value (temporary) |
is_anti_whale | has_trading_cap, has_position_cap | True if either is True |
anti_whale_modifiable | trading_cap_can_be_modified, position_cap_can_be_modified | True if either is True |
hidden_owner | allowance_bypass, privileged_spenders | True when the allowance-bypass probe named at least one address |
owner_change_balance | owner_can_change_balance | Same value |
Fields Without Direct Equivalent (returns null)
| GoPlus Field | Reason |
|---|---|
cannot_sell_all | Not currently tracked |
can_take_back_ownership | Not currently tracked |
gas_abuse | Not currently tracked |
personal_slippage_modifiable | Not currently tracked |
lp_total_supply | Not currently tracked |
is_airdrop_scam | Not currently tracked |
trust_list | Not currently tracked |
holder_count | Not currently tracked |
owner_balance | Not currently tracked |
owner_percent | Not currently tracked |
Caveats
-
is_blacklisted/is_whitelisted: Currently both map fromhas_blacklist_or_whitelist. We cannot distinguish between blacklist-only, whitelist-only, or both. -
Boolean Format: GoPlus uses string format (
"1"/"0") instead of booleans. All boolean fields are converted accordingly. -
Tax Terminology: Our
send_taxmaps to GoPlussell_tax, and ourreceive_taxmaps to GoPlusbuy_tax. Note the direction inverts — code computing slippage must follow the rename. -
buy_tax/sell_taxare pool-aware, and may disagree with the contract-level tax. Ingoplusmode these are taken from the first pool index[]that reports a tax, falling back to the contract-levelreceive_tax/send_taxonly when no pool reports one. The pool figure reflects what a real swap would cost and is usually the more useful number, but it meansbuy_taxin agoplusresponse can differ fromreceive_taxin the equivalentdedaubresponse for the same token. If you compare the two formats, expect this and prefer the pool figure for trade decisions. -
is_honeypotis alwaysnull. It is present in the response shape but not yet produced. Integrations that gate trades onis_honeypot === "1"will treat every token as safe after migrating. Derive an equivalent fromcannot_buy, the per-pooldex[].buy_success/sell_success(dedaubmode), and the tax fields — or block while the field is unavailable. See Readingbuy_success/sell_successfirst, because thenull/"pending"states matter for exactly this decision. -
hidden_owneris narrower than it looks: it is"1"only when the bypass is address-specific. A universal bypass — where the allowance check is skipped for everyone — setsallowance_bypassbut leavesprivileged_spendersempty, and maps to"0"because no undisclosed owner exists. It isnullwhen the probe could not run, which is not the same as"0". Use the Dedaub format if you need to tell those three apart, or to read the offending addresses.
Static Analysis Features
We leverage the Gigahorse decompiler to analyze contracts whose source code is unavailable. The decompiled bytecode is checked for patterns commonly associated with undesirable behaviors in ERC20 tokens.
mint_or_burn_function:
There is a public function that changes the token's total supply.
can_selfdestruct:
The contract contains a selfdestruct call. (Note: The EVM no longer supports the SELFDESTRUCT opcode, so this is now a no-op.)
transfer_guarded_by_storage:
The transfer function's execution depends on a storage variable (e.g., a flag or mapping check). This is a signal that transfers can be conditionally blocked by the contract owner or via some internal logic.
transfer_from_guarded_by_storage:
The transferFrom function's execution depends on a storage variable. Same implications as transfer_guarded_by_storage — indicates that delegated transfers can be conditionally blocked.
trading_cooldown:
An address must wait for a certain period after its last transfer before it can transfer tokens again.
view_function_modifies_state:
A function with the signature of one of the view functions defined by EIP-20 updates a storage variable.
has_trading_cap:
The amount of tokens transferred by a single call to transfer cannot exceed a value, either constant or held in storage.
has_trading_cap_functionality:
There is code that could enforce a trading cap. This can be true even if the cap is disabled.
trading_cap_can_be_modified:
The transfer amount cap is held in storage and there is a function that can update it.
has_position_cap:
The amount of tokens an address can hold cannot exceed a value, either constant or held in storage.
has_position_cap_functionality:
There is code that could enforce a position cap. This can be true even if the cap is disabled.
position_cap_can_be_modified:
The position cap is held in storage and there is a function that can update it.
has_owner_functionality:
There is functionality that could let one or more addresses have access to functionality of the token that the rest do not. This can be true even if the owner has been renounced.
owner_address:
The address of the owner.
has_pause_functionality:
The token has code that could pause trading. Can be true even if the current state of the contract does not allow pausing functionality to be re-enabled.
pause_status_can_be_modified:
The pause status can be changed in the future.
has_blacklist_or_whitelist:
Addresses can be restricted from trading the token.
external_call:
There is a call to another contract, whose target is not statically known and/or returns arbitrary data that are used by the token.
timebomb:
Certain functionality is only enabled after a specific timestamp.
has_tax_functionality:
This token has a tax functionality implemented. This could be true even if the current tax is 0 as the taxes could have been wiped.
tax_can_be_modified:
Tax value can be updated.
is_open_source:
This indicates whether the contract's source code is available.
has_open_source_implementation:
For proxy contracts: indicates whether the implementation contract's source code is verified. Only relevant when is_proxy is true. null for non-proxy contracts.
is_proxy:
Indicates if the contract is a proxy contract.
balance_update_function:
A list of function selectors (e.g., "0xa9059cbb") that modify the token's balance storage slot. Expected selectors are transfer and transferFrom; additional selectors may indicate hidden mint/burn or fee-skimming logic.
"balance_update_function": [
"0xa9059cbb",
"0x23b872dd"
]
allowance_update_function:
A list of function selectors that modify the token's allowance storage slot. Expected selectors include approve, transferFrom, increaseAllowance, and decreaseAllowance; unexpected selectors may indicate allowance manipulation.
"allowance_update_function": [
"0x095ea7b3",
"0x23b872dd",
"0x39509351",
"0xa457c2d7"
]
Simulation Features
transfer_success:
A test transfer was simulated on-chain. If false, the transfer failed — the token may block transfers entirely (potential honeypot).
allowance_bypass:
true when some caller can move another holder's tokens through transferFrom without ever having been approved — the shape of a hidden drain backdoor.
Detection is a differential simulation rather than a pattern match, because a backdoor address is rarely a plain literal in the code. Candidate callers are drawn from the declared owner, the address literals in the runtime bytecode, and the ABI-encoded address arguments in the constructor tail of the creation bytecode. Each candidate is simulated calling transferFrom against a control caller that provably holds no approval:
| Control caller | Candidate | Verdict |
|---|---|---|
| reverts | succeeds | true — address-specific bypass, candidate listed in privileged_spenders |
| succeeds | — | true — universal bypass; the token does not enforce allowances at all |
| reverts | reverts | false — allowances are enforced |
⚠ Tri-state. null means the probe could not run (no suitable holder, or the node did not answer) and must not be rendered as safe. Only false is an all-clear.
privileged_spenders:
The addresses observed moving a holder's balance with zero allowance. Empty when allowance_bypass is false, and also when the bypass is universal rather than address-specific — so read it together with allowance_bypass, never on its own.
"allowance_bypass": true,
"privileged_spenders": [
"0x961c7a5dde069aa1a4ea73e5b506e4312105f08d"
]
transfer_tax:
send_tax(%): Calculated as the percentage loss from the sender's balance — e.g., if the sender sent 100 tokens and 110 were deducted, the send tax is 10%.receive_tax(%): Calculated as the percentage the recipient received — e.g., if the sender sent 100 tokens and the recipient only received 90, the receive tax is 10%.- All values are expressed as percentages (e.g., 0.0 means 0%).
dex:
An array of liquidity pools where the token is paired with one of the chain's quote tokens — the wrapped native or a canonical stablecoin — along with details on liquidity and trading behavior. Launchpad bonding curves are reported here too, as synthetic pools.
{
"pair": "0x...",
"liquidity_type": "UniV2",
"liquidity": 2455608.22,
"buy_success": true,
"buy_tax": 0.0,
"sell_success": true,
"sell_tax": 0.0,
"errorMessage": null,
"errorCode": null,
"pool_id": null,
"is_launchpad": null
}
-
pair: Address of the liquidity pair. -
liquidity_type: Type of the pool:Type Description UniV2Uniswap V2 or compatible fork UniV3Uniswap V3 or compatible fork UniV4Uniswap V4 or compatible fork SolidlyAerodrome (more Solidly forks coming!) SolidlyV3Aerodrome Slipstream four.memeFour.Meme bonding curve (BSC) flapFlap.sh bonding curve (BSC, Robinhood Chain) trench.todayTrench bonding curve (Robinhood Chain) The last three are bonding curves rather than DEX pools; they always carry
is_launchpad: true.Uniswap V4 pools are simulated like V2 and V3.
buy_success,sell_success,buy_taxandsell_taxare populated forUniV4pools, including pools quoted in the chain's native coin and pools sitting behind a custom hook. Earlier releases returned no buy/sell result for V4 and reported every V4 pool as 0% tax; a client that cached those values, or that special-cased V4 as "tax unknown, assume zero", should stop doing so. A hook that taxes a swap now shows up inbuy_tax/sell_taxlike any other tax. -
liquidity: USD value of the pool's known-token reserves:- Estimated using
balanceOf()on the known token — while not exact, it is a practical approximation that serves as a useful indicator of pool health and liquidity status.
- Estimated using
-
buy_success: Whether buying the token (e.g., WETH -> token) succeeded. Four-valued — see below. -
buy_tax(%): Tax incurred on a buy.nullor"pending"on the same terms asbuy_success. -
sell_success: Whether selling the token succeeded. Four-valued, same asbuy_success. -
sell_tax(%): Tax incurred on a sell. -
errorMessage/errorCode: Populated for failed queries (e.g., low liquidity, failed swaps). -
pool_id: ID of the pool in case of Uniswap V4, otherwisenull. -
is_launchpad:trueif this pool is from a launchpad bonding curve (e.g., four.meme, flap.sh, trench.today).nullotherwise.
Reading buy_success / sell_success — null is not a failure
These four fields are not booleans. Each carries one of four values, and conflating the last two with false is the most common way to misread a Tok{In} response:
| Value | Meaning | Safe reading |
|---|---|---|
true | Simulated, and the trade succeeded | Tradeable on this pool |
false | Simulated, and the trade failed | A real negative signal |
"pending" | Simulation is still running | Not yet known — poll again |
null | No simulation was attempted for this pool | Unknown, carries no signal |
null occurs where a swap simulation does not apply or was not run — bonding-curve pools are the common case, since a curve is not swapped against like a DEX pool.
⚠ Do not render null or "pending" as "cannot buy" or "cannot sell". A client that treats these as falsy will report a healthy token as unsellable. Branch on === true and === false explicitly and handle the other two as "unknown". If you need a single tradeability verdict, require sell_success === false before calling a token unsellable, not merely "not true".
Because these fields are typed as boolean-or-string, statically typed clients should model them as a union (e.g. bool | "pending" | None) rather than bool.
is_liquid:
This boolean indicates if the token is liquid, i.e. if at least one pool has more than $100 of value in it, measured on the chain's quote token side — WETH/USDC/USDT on most chains, WETH/USDG/USDE on Robinhood Chain.
Notes on liquidity
- For V2 pools, using the known token side (e.g., WETH in WETH/XYZ) is reliable since the reserves are fixed.
- For V3 pools, the
balanceOf()method on the known token offers a fast heuristic to assess pool liquidity. While it has some limitations — such as including uncollected fees or dust and not reflecting active price ranges — it still provides a strong signal of pool activity and health. In most cases, very low balances are a reliable indicator of illiquidity or abandoned pools. - For V4 pools,
balanceOf()on the pool address is not viable — all token balances are held by a singlePoolManagercontract, not per-pool contracts. Liquidity is currently fetched via theCoinGeckoAPI using the pool ID (Base token Liquidity $ / Quote token Liquidity $). - For launchpad bonding curve pools (four.meme, flap.sh, trench.today), liquidity is derived from the on-chain reserve data of the bonding curve contract, converted to USD using the quote token price. Curves that quote in the chain's native coin rather than naming a quote token — trench.today, and flap on Robinhood Chain — are valued against the wrapped native.
- When neither side of a pool is a chain anchor, it can still be valued if a price is available for one side via
DefiLlamaenrichment. This is what makes Robinhood Chain's tokenised equities usable as quote assets: a launchpad pool quoted inCOIN,TSLA,GOOGLorSPCXcarries real, reportable liquidity. A pool with no priced side at all is dropped rather than reported at $0.
Launchpad Detection
Tok{In} detects tokens issued by launchpad platforms and reports which platform issued them, on every supported chain.
| Platform | Slug | Chains |
|---|---|---|
| Ape.store | ape.store | Ethereum, BSC, Base, Robinhood Chain |
| Arena | arena | Avalanche, Robinhood Chain |
| ArrowPad | arrowpad | Robinhood Chain |
| Bags | bags | Robinhood Chain |
| bow.fun | bow.fun | Robinhood Chain |
| Circus | circus | Robinhood Chain |
| Clanker | clanker | Ethereum, BSC, Base, Arbitrum, Robinhood Chain |
| Coinbarrel | coinbarrel | Robinhood Chain |
| Doppler (bankr, long.xyz) | doppler | Base, Robinhood Chain |
| DYOR Fun | dyor.fun | Base, Robinhood Chain |
| Flap.sh (bonding curve) | flap | BSC, Base, Robinhood Chain |
| Four.Meme (bonding curve) | four.meme | BSC, Base, Arbitrum |
| HoloLaunch (Holoworld) | hololaunch | BSC, Robinhood Chain |
| hood.fun | hood.fun | Robinhood Chain |
| hyper.meme | hyper.meme | Robinhood Chain |
| Klik | klik | Ethereum, BSC, Robinhood Chain |
| LaunchHood | launchhood | Robinhood Chain |
| LeaveHood | leavehood | Robinhood Chain |
| Lemon.fun | lemon.fun | Robinhood Chain |
| letscash.fun | letscash.fun | Robinhood Chain |
| Livo | livo | Ethereum, Robinhood Chain |
| Lunch.fun | lunch.fun | Robinhood Chain |
| Memecoin.fun | memecoin.fun | Robinhood Chain |
| MintFast | mintfast | Robinhood Chain |
| Noxa | noxa | Robinhood Chain |
| o1.exchange | o1.exchange | Robinhood Chain |
| ORO | oro | Robinhood Chain |
| Pew.fun | pew.fun | Robinhood Chain |
| Pmav.fun | pmav.fun | Robinhood Chain |
| Pons | pons | Robinhood Chain |
| Pons (unofficial fork) | pons-fork | Robinhood Chain |
| Potato.fm | potato.fm | Robinhood Chain |
| Real.fun | real.fun | Robinhood Chain |
| Robinfun | robinfun | Robinhood Chain |
| Stoxes | stoxes.fun | Robinhood Chain |
| Sushi | sushi | BSC, Robinhood Chain |
| Trench (bonding curve) | trench.today | Robinhood Chain |
| varo.rialto | varo.rialto | Robinhood Chain |
| Virtuals Protocol | virtuals | Base, Robinhood Chain |
| Unrecognised launchpad | launchpad | All chains |
Notes on reading the slugs:
- One slug per platform, not per product line. Where a platform runs several launch routes — Pons V1/V2, Stoxes and its RWA line, o1's crypto- and stock-quoted routes, DYOR Fun's versions — they all report the platform's single slug. Screeners that list those routes separately will show more entries than this table does.
pons-forkis an independent operator running the same Pons launcher template. Its tokens look like official Pons tokens, but the launcher belongs to someone else, so it is labelled separately — launchpad reputation is the point of the field, and conflating the two would defeat it.launchpadis a deliberate catch-all. A token that proves it came off a launchpad but whose platform we cannot yet name is still reported as launchpad-issued rather than dropped. Treat it as "yes, launchpad — platform unknown", not as a specific platform.- A platform on one chain is not the same product on another. Ape.store, Clanker, Arena and Virtuals are native to Base, Avalanche or Ethereum and also deployed on Robinhood Chain; the slug is the same, the deployment is not.
Detection is exact — it identifies the contract that issued the token, rather than guessing from the token's address. The one exception is documented below.
Graduated tokens (BSC). Four.Meme and Flap tokens mine a vanity address suffix (4444, ffff, 7777, 8888). That suffix is used as a last-resort fallback on BSC only, for tokens that have left their bonding curve for a standard DEX such as PancakeSwap. It is a pattern match rather than an exact one, so it can occasionally label a token whose address ends that way by coincidence; the exact signals are always preferred when available.
Bonding curves appear as pools. Four.Meme, Flap and Trench hold their reserves in a curve contract, so a token still on its curve appears in the dex array with liquidity_type set to the platform slug and is_launchpad: true. Every other platform in the table seeds a standard Uniswap pool at launch, so its tokens carry is_launchpad_token: true while their pools look like ordinary V2/V3/V4 pools.
How a platform is identified
For a curve platform the curve contract itself is the evidence. For everything else the pool is an ordinary Uniswap pool that pair discovery already finds, so only the label is missing, and four exact signals supply it — tried cheapest-first, so most tokens are labelled without an extra RPC call:
| # | Signal | Cost | Example platforms |
|---|---|---|---|
| 1 | Token deployer | Free — already on token metadata | Virtuals |
| 2 | Runtime bytecode hash | Free — reuses the static-analysis md5 | Trench |
| 3 | Minimal-proxy implementation | One eth_getCode | Flap, Doppler, varo.rialto |
| 4 | An issuer-naming view call | One eth_call per name tried | Pons, Noxa, bow.fun, letscash.fun, and new launchpads on those templates |
All four are exact matches, not heuristics — the vanity-suffix fallback described above is the only pattern match in the set, and it applies on BSC only.
Signal 4 is where most launchpads land, and templates disagree on what to call the view: Pons and Noxa expose launchFactory(), varo.rialto and bow.fun expose launchpad(), letscash.fun exposes factory(). Each name is tried in turn and the first that names a known launchpad wins.
Signal 4 is also the only one with a generic fallback — an issuer that answers but is not recognised still reports launchpad rather than nothing, which is what keeps a newly launched platform from being silently missed. That fallback is per view name, not blanket: factory() is excluded from it, because liquidity-pool tokens answer factory() with their DEX factory, and treating that as evidence would label every Uniswap LP token on the chain as launchpad-issued.
A platform can match more than one signal. Flap on Robinhood Chain is identifiable by its clone implementation and runs a curve whose reserves are worth reporting as a pool.
Related Fields
is_launchpad_token: tri-state.true— the token was issued by a launchpad platform.false— pools were resolved for this token and none of them, nor any provenance signal, indicates a launchpad.null— unknown. No pools were resolved, so no determination could be made. Do not read this as "not a launchpad".
launchpad_name: The launchpad platform slug (e.g."four.meme","pons","doppler", or the generic"launchpad").nullifis_launchpad_tokenis nottrue.is_launchpad(per pool):trueif the specific pool is a launchpad bonding curve.nullotherwise. A launchpad token that graduated, or that never ran a curve, hasis_launchpad_token: truewhile every pool indexhasis_launchpad: null— the two fields answer different questions.
Supply & Burn Features
decimals:
Number of decimal places the token uses (e.g., 18 for most ERC20 tokens). Used to convert between raw and human-readable amounts.
supply:
Total supply of the token, normalized by decimals (raw_supply / 10^decimals). This is the human-readable value (e.g., 1000000000 for a token with 1 billion supply).
burned_supply:
Total supply held by known burn addresses (0x000...0000 and 0x000...dead), normalized by decimals. The value is verified on-chain via multicall. 0 if no tokens have been sent to burn addresses.
burned_supply_pct:
Percentage of the total supply held by known burn addresses, capped to 2 decimal places (e.g., 6.13). 0 if no tokens have been burned.
circulating_supply:
Circulating supply of the token, calculated as supply - burned_supply. Represents the tokens that are still in active circulation.
burn_addresses:
An array of burn address balances. Each entry is a holder object with the burn address and its token balance. Only addresses with a balance > 0 are included.
"burn_addresses": [
{
"owner_address": "0x000000000000000000000000000000000000dead",
"token_amount": 61256610.0,
"pct_held": 6.13
}
]
normalized_supply (deprecated):
Use supply instead. Returns the same value as supply. Kept for backwards compatibility.
Other Features
name:
Name of the token.
symbol:
Symbol of the token.
creator_address:
Address of the creator (EOA) of the token.
creator_balance:
Amount of tokens held by the creator (normalized by decimals).
creator_percent:
Percentage of the total supply held by the creator of the token, capped to 2 decimal places.
top_holders:
Top 10 token holders, returned as an array of holder objects. Balances are verified on-chain via multicall.
[
{
"owner_address": "0x...",
"token_amount": 1234567.89,
"pct_held": 12.34
}
]
Each entry in top_holders has the following fields:
owner_address: Address of the token holder.token_amount: Amount of tokens held by that address (normalized by decimals).pct_held: Percentage of the total token supply held by that address.
is_in_dex:
The token is in at least one DEX.
cannot_buy:
Returns true if the token cannot be bought through any of the listed pools or if all the pools are too illiquid (less than $100).
owner_can_change_balance:
The token owner can modify any holder's balance directly. This field is part of the extended analysis set and may be absent from the response entirely — an absent field means "not assessed", which is not the same as false. Key off its presence before reading it.
is_launchpad_token:
true if the token was issued by a launchpad platform. false if pools were resolved and none indicates a launchpad. null if no pools were resolved, meaning no determination could be made — see Launchpad Detection for the full platform list.
launchpad_name:
The launchpad platform slug (e.g. "four.meme", "clanker", "doppler", "pons"). Falls back to the generic "launchpad" when the token is provably launchpad-issued but the platform is not yet named. null if is_launchpad_token is not true. The full list of slugs is in Launchpad Detection — treat it as an open set and keep a default branch, since new platforms are added over time.
last_scanned_at:
ISO 8601 UTC timestamp of when this token was last analyzed (e.g., "2025-01-15T12:30:00Z").
Token Safety Summary
The summary endpoint returns a compact, pre-processed view of a token's safety data — designed for dashboards and quick-scan UIs where you need actionable signals without parsing the full feature set.
curl -X 'GET' \
'https://tokin-api.dedaub.com/token/{chain}/{token_address}/summary' \
-H 'accept: application/json' \
-H 'X-API-Key: YOUR-API-KEY'
Response Fields
active_flag_count:
The number of risk signals currently active across the token's static analysis and simulation features. A flag is counted as active when it indicates a potential risk — for example, has_blacklist_or_whitelist: true or transfer_success: false. is_open_source: true is not counted (safe state), and neither is a flag whose value is null — an unknown is not a risk signal, so allowance_bypass: null leaves the count unchanged.
major_flags:
A curated list of three key safety signals covering the most impactful risk categories. Each flag has:
label: Human-readable name.description: What the flag means.value:trueif the condition is present,falseif not,nullif unknown.
| Label | Derived From | Active When |
|---|---|---|
Access Restrictions | has_blacklist_or_whitelist | true |
Transfer Restrictions | has_trading_cap_functionality or has_position_cap_functionality | either is true |
Has enough liquidity | is_liquid | false (lack of liquidity is the risk) |
detected_pool_count:
Total number of liquidity pools found for the token.
dominant_pool:
The pool with the highest liquidity, or null if no pools exist.
type: Pool type (e.g.UniV2,UniV3).address: Pool contract address.liquidity: Liquidity in USD.can_buy/can_sell: Whether buys/sells succeed in this pool.buy_tax/sell_tax: Tax as a percentage (e.g.1.0= 1%).nullif unavailable.
Example
curl -X 'GET' \
'https://tokin-api.dedaub.com/token/ethereum/0x2328434559f7dec44373822cf68052de0d671b7f/summary' \
-H 'accept: application/json' \
-H 'X-API-Key: YOUR-API-KEY'
Result:
{
"active_flag_count": 9,
"major_flags": [
{
"label": "Access Restrictions",
"description": "Addresses can be restricted from trading the token. This could be a blacklist, whitelist, or both.",
"value": true
},
{
"label": "Transfer Restrictions",
"description": "The contract limits how much can be traded in a single transaction or held by a single address.",
"value": true
},
{
"label": "Has enough liquidity",
"description": "The token has at least one WETH, USDC, or USDT pair with more than $100 in liquidity.",
"value": true
}
],
"detected_pool_count": 1,
"dominant_pool": {
"type": "UniV2",
"address": "0xa954213fb0F410c8f1b2fca9B65F11A63ab3fFdb",
"liquidity": 59292.71,
"can_buy": true,
"can_sell": true,
"buy_tax": 1.0,
"sell_tax": 1.0
}
}
Example
curl -X 'GET' \
'https://tokin-api.dedaub.com/token/ethereum/0x2328434559f7dec44373822cf68052de0d671b7f' \
-H 'accept: application/json' \
-H 'X-API-Key: YOUR-API-KEY'
Result:
{
"chain": "ethereum",
"contract_address": "0x2328434559f7dec44373822cf68052de0d671b7f",
"features": {
"mint_or_burn_function": true,
"can_selfdestruct": false,
"transfer_guarded_by_storage": true,
"transfer_from_guarded_by_storage": true,
"trading_cooldown": false,
"view_function_modifies_state": false,
"has_blacklist_or_whitelist": true,
"external_call": true,
"timebomb": true,
"has_pause_functionality": true,
"pause_status_can_be_modified": false,
"has_owner_functionality": true,
"has_tax_functionality": true,
"tax_can_be_modified": false,
"has_trading_cap_functionality": true,
"trading_cap_can_be_modified": false,
"has_position_cap_functionality": true,
"position_cap_can_be_modified": false,
"balance_update_function": [
"0xa9059cbb",
"0x23b872dd",
"0xcd05e4ad",
"0x3bd5d173"
],
"allowance_update_function": [
"0xa9059cbb",
"0x23b872dd",
"0x39509351",
"0xa457c2d7",
"0x95ea7b3"
],
"name": "Simon the Gator",
"symbol": "SIMON",
"decimals": 18,
"supply": 690000000.0,
"normalized_supply": 690000000.0,
"burned_supply": 0.0,
"burned_supply_pct": 0.0,
"circulating_supply": 690000000.0,
"is_open_source": true,
"has_open_source_implementation": null,
"is_liquid": true,
"is_proxy": true,
"owner_address": "0x0000000000000000000000000000000000000000",
"send_tax": 0,
"receive_tax": 1,
"transfer_success": true,
"allowance_bypass": false,
"privileged_spenders": [],
"creator_address": "0x9cb1a958ad57f59a91582b81ac88378c1c12ee58",
"creator_balance": 400000,
"creator_percent": 0.0,
"is_in_dex": true,
"cannot_buy": false,
"is_launchpad_token": false,
"launchpad_name": null,
"has_trading_cap": true,
"has_position_cap": false,
"last_scanned_at": "2025-06-15T10:22:33Z",
"dex": [
{
"errorMessage": null,
"errorCode": null,
"pair": "0xa954213fb0F410c8f1b2fca9B65F11A63ab3fFdb",
"liquidity_type": "UniV2",
"liquidity": 59292.7097022566,
"buy_success": true,
"buy_tax": 1,
"sell_success": true,
"sell_tax": 1,
"pool_id": null,
"is_launchpad": null
}
],
"top_holders": [],
"burn_addresses": []
},
"error": ""
}
API Response Reference
All endpoints use JSON unless otherwise noted. Authentication endpoints redirect via 307.
Common Response Codes
| Code | Meaning |
|---|---|
200 | Success |
307 | Redirect (OAuth flows) |
400 | Bad request (invalid input, failed precondition) |
401 | Missing or invalid API key / unauthenticated |
404 | Resource not found |
409 | Conflict (e.g. max API keys reached) |
422 | Validation error (invalid address format, missing params) |
429 | Rate limit or quota exceeded |
500 | Internal server error |
503 | Service not configured (billing) |
Token
GET /token/{chain}/{contract_address}
Returns token safety features.
| Code | Body / Detail |
|---|---|
200 | TokenResponse with features populated |
422 | TokenResponse with error: "Contract address needs to be EVM compatible" |
422 | TokenResponse with error: "Error validating item data: ..." |
500 | TokenResponse with error: "Unable to retrieve safety features for this token" |
500 | TokenResponse with error: "Error fetching items: ..." |
401 | {"detail": "API Key is missing"} or {"detail": "Invalid API Key"} |
429 | See Rate Limiting |
TokenResponse schema:
{
"chain": "ethereum",
"contract_address": "0x...",
"features": { ... },
"error": ""
}
Query param response_format=goplus returns GoPlus-compatible features shape.
GET /token/{chain}/{contract_address}/summary
Returns a compact safety summary. Uses the same cache as the full token endpoint.
| Code | Body / Detail |
|---|---|
200 | TokenSummaryResponse |
422 | TokenSummaryResponse with zeroes/nulls (invalid or burn address) |
500 | TokenSummaryResponse with zeroes/nulls (analysis failure) |
401 | {"detail": "API Key is missing"} or {"detail": "Invalid API Key"} |
429 | See Rate Limiting |
TokenSummaryResponse schema:
{
"active_flag_count": 9,
"major_flags": [
{
"label": "Access Restrictions",
"description": "...",
"value": true
}
],
"detected_pool_count": 1,
"dominant_pool": {
"type": "UniV2",
"address": "0x...",
"liquidity": 59292.71,
"can_buy": true,
"can_sell": true,
"buy_tax": 1.0,
"sell_tax": 1.0
}
}
Auth
GET /auth/login/{provider}
| Code | Detail |
|---|---|
307 | Redirect to OAuth provider (Google or GitHub) |
GET /auth/callback/{provider}
| Code | Detail |
|---|---|
307 | Redirect to {FRONTEND_URL}/auth/success?token=<jwt> on success |
400 | OAuth callback failure (provider error) |
Profile
All profile endpoints require a valid JWT (set via cookie from auth callback).
GET /profile/me
| Code | Body |
|---|---|
200 | User object |
401 | {"detail": "Not authenticated"} |
GET /profile/api-keys
| Code | Body |
|---|---|
200 | Array of APIKey objects |
401 | {"detail": "Not authenticated"} |
POST /profile/api-keys
| Code | Body / Detail |
|---|---|
200 | Created APIKey object |
401 | {"detail": "Not authenticated"} |
409 | {"detail": "Maximum of N API key(s) allowed per user"} |
500 | {"detail": "Error creating API key"} |
DELETE /profile/api-keys/{api_key_id}
| Code | Body |
|---|---|
200 | {"message": "API key deleted successfully"} |
200 | {"message": "API key not found or does not belong to the user"} (no-op) |
401 | {"detail": "Not authenticated"} |
GET /profile/logs
| Code | Body |
|---|---|
200 | Array of APILog objects |
401 | {"detail": "Not authenticated"} |
GET /profile/logs_status_codes
| Code | Body |
|---|---|
200 | Array of {"status_code": int, "count": int} |
401 | {"detail": "Not authenticated"} |
GET /profile/logs_chains
| Code | Body |
|---|---|
200 | Array of {"chain": string, "count": int} |
401 | {"detail": "Not authenticated"} |
GET /profile/logs_addresses
| Code | Body |
|---|---|
200 | Array of {"address": string, "count": int} |
401 | {"detail": "Not authenticated"} |
GET /profile/linked-accounts
| Code | Body |
|---|---|
200 | Array of {"id", "provider", "provider_id", "email", "linked_at"} |
401 | {"detail": "Not authenticated"} |
DELETE /profile/linked-accounts/{account_id}
| Code | Body / Detail |
|---|---|
200 | {"message": "Account unlinked successfully"} |
200 | {"message": "Account not found or does not belong to user"} (no-op) |
400 | {"detail": "Cannot unlink last provider account"} |
401 | {"detail": "Not authenticated"} |
Rate Limiting
429 responses are returned by the auth middleware for API-key-authenticated endpoints. Three independent checks:
| Check | Scope | Headers |
|---|---|---|
| Per-minute QPS | Per API key (Redis) | Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining: 0 |
| Daily quota | Per user (Redis) | X-Quota-Daily-Limit, X-Quota-Daily-Used, Retry-After |
| Monthly quota | Per user (PostgreSQL) | X-Quota-Monthly-Limit, X-Quota-Monthly-Used |
Admin users bypass all limits. Requests from app.dedaub.com bypass auth and rate limiting entirely.