The Ebisu's Bay API gives developers direct access to the marketplace on Cronos: browse listings, collections, wallet holdings, and market-wide stats over plain REST — and fill marketplace orders on-chain through the gasless-listing validator. No SDK required.
Base URL: https://api.ebisusbay.com
All browse endpoints are plain HTTPS GETs that return JSON. No authentication is required. CORS is open (Access-Control-Allow-Origin: *) and strong ETags are emitted, so conditional GETs (If-None-Match) work.
Optional API key. If you have a registered partner key you may send it in the eb-api-key header (the x-api-key header name is also accepted). Today the key's only effect is rate-limit tiering: recognized keys are effectively exempt from the app-side courtesy limiter (unauthenticated traffic is capped at 250 requests / 5 minutes per IP-or-key; the API advertises this via standard RateLimit-* headers). The key never changes response contents. App-side limiting is being phased out in favor of proxy-level throttling — requests may be throttled at the edge; handle 429/503 with backoff regardless of whether you send a key.
Pagination. All list endpoints are page/pageSize paginated and cap pageSize at 100. Requesting pageSize > 100 adds an advisory response header X-PageSize-Warning: pageSize will be capped at 100 in a future release.
Freshness. Aggregate endpoints (/collections, /marketdata, collection stats) are served from short server-side caches (roughly 30–60 s; /marketdata's underlying aggregate is rematerialized about every 5 minutes). Treat figures as near-real-time, not tick-exact.
Chain scope. The marketplace data is Cronos mainnet (chain = 25, name cronos). Chain ids 1 / 338 / 282 / 388 are accepted by validation for legacy compatibility, but only chain 25 returns listing data.
The following legacy paths are retired and answer HTTP 410 with the stable body {"status":410,"error":"DEPRECATED_ENDPOINT"}: GET /auctions, GET /farms, GET /farmbalance, GET /bundle, POST /bundle, GET /api/supply/circulating.
GET /wallets (v1) answers HTTP 301 with a JSON body directing callers to /v2/wallets (no Location header). GET /getLeaders remains as a decommissioned stub that always returns an empty result set.
GET https://api.ebisusbay.com/listings
Browse active (and historical) marketplace listings. This is the primary feed for integrators: it returns full listing rows — price, currency, seller, state, timestamps — joined with token metadata and collection info. By default it serves only presentable listings: not marked invalid, not expired (unless already sold/cancelled), and belonging to non-blacklisted collections. The listingId values returned here are exactly what the gasless validator accepts.
| name | type | required | default | constraints / notes |
|---|---|---|---|---|
listingId | string (CSV allowed) | no | — | exact listing id(s); comma-separate for multiple |
collection | address (CSV allowed) | no | — | filter by NFT contract address(es), case-insensitive |
tokenId | string (CSV allowed) | no | — | filter by token id within a collection |
seller | address | no | — | lowercased before matching |
purchaser | address | no | — | lowercased before matching |
state | int 0|1|2 | no | — | 0 = active, 1 = sold, 2 = cancelled; other values → 400 |
currency | address (CSV allowed) | no | — | payment token; 0x000…0 = native CRO |
chain | int or name | no | 25 | one of 1,25,338,282,388 or ethereum,cronos,cronos-testnet,cronos-zk-testnet,cronos-zk; unsupported numeric id → 400. Only 25 returns data. |
page | int | no | 1 | 1-based |
pageSize | int | no | 100 | capped at 100 |
sortBy | enum | no | listingTime | listingId, listingTime, saleTime, price, rank, expirationDate, amount, pricePerItem. price/pricePerItem sort by USD value. expirationDate, amount and pricePerItem require direction, otherwise ordering is unspecified. |
direction | enum | no | desc (when sortBy given) | asc | desc (exact, lowercase) |
verified | 1|0 | no | — | 1 = only verified collections, 0 = only unverified |
showHidden | 1|0 | no | — | 1 = only unlistable (hidden) collections, 0 = only listable |
blacklist | int (CSV allowed) | no | 0,1,4 | collection blacklist codes to include |
minPrice / maxPrice | number | no | — | inclusive bounds on the listing price (listing currency units) |
minListingTime / maxListingTime | unix seconds | no | — | inclusive bounds |
minSaleTime / maxSaleTime | unix seconds | no | — | inclusive bounds |
minRank / maxRank | int | no | — | inclusive rarity-rank bounds |
search | string/number | no | — | numeric values match token id or exact rank; free-text search is currently non-functional (see caveats) |
invalid | — | no | — | accepted for legacy compatibility; has no effect |
traits / powertraits | JSON object | no | — | accepted for legacy compatibility; currently non-functional on this endpoint — for working trait filtering use /fullcollections. Malformed JSON → 500 |
# Active listings, newest first (default sort)
curl "https://api.ebisusbay.com/listings?state=0&pageSize=10"
# Filter by collection + price range, cheapest first
curl "https://api.ebisusbay.com/listings?state=0&collection=0x719fdfb0ba006747a83438cc8900c8a2b35e0aff&sortBy=price&direction=asc"
Envelope (key order as served):
| field | type | description |
|---|---|---|
status | int | mirrors the HTTP status (200 on success) |
error | string | present only when a validation problem occurred |
page | int | echoed page |
totalCount | int | total matching rows |
pageSize | int | effective page size (≤100) |
totalPages | int | ceil(totalCount / pageSize) |
listings | array | the page of listing objects |
Each listing object:
| field | type | description |
|---|---|---|
listingId | string | listing id — decimal for legacy Port listings, 0x… 32-byte digest for gasless listings; feed this to the validator |
nftId | string | token id |
nftAddress | address | NFT contract |
chain | int | chain id (25) |
valid | bool | false when the listing was flagged invalid (invalid > 1) |
amount | string | quantity listed (ERC1155 can be > 1) |
invalid | int | raw invalidity flag (0/1 pass the default filter) |
seller | address | lister |
purchaser | address|null | buyer once sold (zero address until then) |
state | int | 0 active / 1 sold / 2 cancelled |
price | string | human-decimal price in currency units (NOT wei) |
currency | address | payment token; 0x000…0 = native CRO |
royalty | string | royalty amount |
fee | string | marketplace fee amount |
saleTime | string | unix seconds of sale as string; "0" when unsold |
listingTime | string | unix seconds the listing was created |
expirationDate | string|null | unix seconds, or null for no expiry (legacy) |
transactionHash | string|null | listing tx hash if known |
nft | object | token metadata: nftId, nftAddress, chain, then metadata fields (edition, name, image, original_image, animation_url, description, score, rank, attributes, …) — only populated keys appear |
collection | object | { address, name, slug, metadata } where metadata carries avatar/banner/card CDN URLs and socials |
Real (live) example — GET /listings?state=0&pageSize=1 (nft/collection metadata trimmed; real responses carry the full attribute list and socials):
{
"status": 200, "page": 1, "totalCount": 49738, "pageSize": 1, "totalPages": 49738,
"listings": [{
"listingId": "11", "nftId": "273",
"nftAddress": "0xce5cac89e25dbccd590090994919a5ef53bbd6c0",
"chain": 25, "valid": true, "amount": "1", "invalid": 1,
"seller": "0x7ff3d5cc69766652495ab5c3323224cf6e2b3229",
"purchaser": "0x0000000000000000000000000000000000000000",
"state": 0, "price": "777",
"currency": "0x0000000000000000000000000000000000000000",
"royalty": "38.85", "fee": "23.31", "saleTime": "0",
"listingTime": "1639048510", "expirationDate": null, "transactionHash": null,
"nft": {
"nftId": "273", "nftAddress": "0xce5cac89e25dbccd590090994919a5ef53bbd6c0", "chain": 25,
"edition": 273, "name": "CronosEyez #273",
"image": "https://ipfs.ebisusbay.com/ipfs/QmfAUP7PzD1s5qHiprZYD1ZsSgogMEGoSCdxT7qbsYvicQ/273.png",
"rank": 146,
"attributes": [{ "value": "dark ray", "trait_type": "Background" }]
},
"collection": {
"address": "0xce5cac89e25dbccd590090994919a5ef53bbd6c0",
"name": "CronosEyezNFT", "slug": "cronoseyez",
"metadata": { "avatar": "https://cdn-prod.ebisusbay.com/files/collection-images/cronoseyez/gen1/avatar.jpg" }
}
}]
}
| status | body | when |
|---|---|---|
| 400 | {"status":400,"error":"Invalid chain specified"} | unsupported numeric chain |
| 400 | {"status":400,"error":"Invalid state specified - should be 0 (active), 1 (sold) or 2 (cancelled)"} | non-numeric or out-of-range state |
| 400 | {"status":400,"error":"sortBy=[listingId,listingTime,saleTime,price,rank,expirationDate,amount,pricePerItem]"} | bad sortBy |
| 400 | {"status":400,"error":"direction=[asc,desc]"} | bad direction |
| 400 | full data envelope with "error":"Invalid parameter specified" | unknown query parameter — legacy quirk: the request still executes and the page of data is returned alongside the error, with HTTP 400 |
| 500 | {"status":500,"error":"Internal Server Error"} | malformed traits/powertraits JSON, or unexpected server error |
| 200 (degraded) | {"status":200} only — no listings, no pagination | inputs the legacy backend treated as a swallowed DB error: page ≤ 0 combined with pageSize, non-numeric blacklist, numeric minAmount/maxAmount, non-numeric maxSaleTime, operator-shaped query values |
listings key.search and traits/powertraits filters are currently non-functional on this endpoint: they return a degraded {"status":200} body without listings. Numeric search (token id / rank) works. For trait filtering, use /fullcollections (optionally with listed=1 to keep only listed tokens).price is a human decimal string in the listing currency — multiply into wei yourself if you need on-chain units (the validator does this for you).chain=388 validates but yields totalCount: 0.chain=foo) is not rejected — it silently disables the chain filter (legacy quirk).invalid > 1, expired-and-still-active listings, and collections outside blacklist codes {0, 1, 4}.GET https://api.ebisusbay.com/fullcollections
Browse every token in a collection — listed and unlisted alike — with per-token market context (active listing, last sale, best offer) and full metadata. Where /listings is a feed of marketplace listings, /fullcollections is the token-first view of a collection, and it is the endpoint that supports trait filtering: pass a traits JSON object to narrow the collection to tokens carrying specific attribute values.
For snapshot-style integrations (allowlists, raffle rolls, bot checks) add idsOnly=1 and the endpoint answers with the complete matching token-id list in a single response — no paging.
| name | type | required | default | constraints / notes |
|---|---|---|---|---|
address | address (CSV allowed) | yes | — | NFT contract address(es), case-insensitive |
traits | JSON object | no | — | trait filter, e.g. {"Background":["Pink","Blue"],"Fur":["Gold"]} — see Trait filtering below |
idsOnly | 1|true | no | off | return only the full matching token-id list (tokenIds) instead of the paginated nfts page; page/pageSize/sortBy/direction are ignored |
token | string (CSV allowed) | no | — | restrict to specific token id(s) |
listed | 1|0 | no | — | 1 = only tokens with an active listing, 0 = only unlisted tokens |
currency | address (CSV allowed) | no | — | keep only tokens whose active listing uses this payment token; 0x000…0 = native CRO |
minPrice / maxPrice | number | no | — | inclusive bounds on the active listing price (listing currency units); tokens without an active listing are dropped |
minListingTime / maxListingTime | unix seconds | no | — | inclusive bounds on the active listing's creation time |
minRank / maxRank | int | no | — | inclusive rarity-rank bounds |
search | string/number | no | — | numeric = token id or exact rank; text = substring match over name/attributes |
burnt | 1|0 | no | — | 1 = only burnt tokens, 0 = only non-burnt |
chain | int or name | no | 25 | same values as /listings; only chain 25 returns data |
blacklist | int (CSV allowed) | no | 0,1,4 | collection blacklist codes to include |
page | int | no | 1 | 1-based |
pageSize | int | no | 100 | capped at 100 |
sortBy | enum | no | id | id, name, rank, price, listingId, listingTime, expirationDate |
direction | enum | no | asc | asc | desc (exact, lowercase) |
powertraits | JSON object | no | — | accepted for legacy compatibility; currently non-functional. Malformed JSON → 500 |
traits is a JSON object mapping trait categories to arrays of accepted values, URL-encoded into the query string:
{"Background":["Pink","Blue"],"Fur":["Gold"]} matches tokens whose Background is Pink or Blue and whose Fur is Gold."pink" does not match "Pink". Take values verbatim from the token metadata's attributes (trait_type / value pairs, as returned by this endpoint or by /nft).{"Level":["5"]}).400.traits JSON (or a non-string value item, e.g. [5] without quotes) → 500 (legacy behavior; fix the JSON).# Complete id list for Background = Pink (one call, no paging)
curl -G "https://api.ebisusbay.com/fullcollections" \
--data-urlencode "address=0x719fdfb0ba006747a83438cc8900c8a2b35e0aff" \
--data-urlencode 'traits={"Background":["Pink"]}' \
--data-urlencode "idsOnly=1"
# Same filter, full NFT objects, paginated
curl -G "https://api.ebisusbay.com/fullcollections" \
--data-urlencode "address=0x719fdfb0ba006747a83438cc8900c8a2b35e0aff" \
--data-urlencode 'traits={"Background":["Pink"]}' \
--data-urlencode "pageSize=50"
With idsOnly=1:
| field | type | description |
|---|---|---|
status | int | 200 on success |
totalCount | int | number of matching tokens |
tokenIds | string | all matching token ids, sorted numerically ascending |
Without idsOnly (default), the paginated envelope:
| field | type | description |
|---|---|---|
status | int | 200 on success |
page / totalCount / pageSize / totalPages | int | pagination (pageSize ≤ 100) |
nfts | array | the page of token objects |
Each token object: id, address, chain, owner, then market (active listing: id, price, currency, seller, expirationDate, uri — empty object when unlisted), last_sale, offer (best open offer), followed by the token's metadata fields (name, image, description, rank, attributes, …).
{
"status": 200, "page": 1, "totalCount": 4, "pageSize": 100, "totalPages": 1,
"nfts": [{
"id": "5",
"address": "0x719fdfb0ba006747a83438cc8900c8a2b35e0aff",
"chain": 25,
"owner": "0x7ff3d5cc69766652495ab5c3323224cf6e2b3229",
"market": { "uri": "https://app.ebisusbay.com/collection/cronos/0x719f…0aff/5" },
"last_sale": {},
"offer": {},
"name": "Example #5",
"image": "https://ipfs.ebisusbay.com/ipfs/Qm…/5.png",
"rank": 146,
"attributes": [{ "trait_type": "Background", "value": "Pink" }]
}]
}
| status | body | when |
|---|---|---|
| 400 | {"status":400,"error":"Required parameter missing - parameters must include one of (address, slug)"} | no address |
| 400 | {"status":400,"error":"Invalid parameter specified - parameters must be one of …"} | unknown query parameter (unlike /listings, no data is returned) |
| 400 | {"status":400,"error":"Invalid chain specified"} | unsupported numeric chain |
| 400 | {"status":400,"error":"Too many trait categories - maximum is 32"} (and similar) | traits selection over the size caps |
| 500 | {"status":500,"error":"Internal Server Error"} | malformed traits/powertraits JSON, non-string trait value items, or unexpected server error |
| 200 (degraded) | {"status":200} only | inputs the legacy backend treated as a swallowed DB error (e.g. non-numeric blacklist, fractional minRank) |
listed, currency and the price/listing-time bounds filter on the active listing, so combining them with idsOnly=1 returns only ids that currently satisfy them — a listing snapshot, not a permanent trait set.minPrice/maxPrice/minListingTime/maxListingTime silently drop tokens without an active listing (legacy semantics).GET https://api.ebisusbay.com/collections
Without an address/collection filter, returns the marketplace's collections ranked by real 30-day volume (descending) — the same ranking the Ebisu's Bay collections page uses — in a detailed per-collection shape: verification, holders/supply, all-time and windowed (1d/7d/30d) stats, metadata and blacklist status.
| name | type | required | default | constraints |
|---|---|---|---|---|
page | int | no | 1 | min 1 |
pageSize | int | no | 50 | clamped to 1, 100 |
chain | int or name | no | 25 | invalid numeric id → 400 |
Unlike most legacy endpoints, the ranked list rejects unknown parameters outright (HTTP 400, {"status":400,"error":"Invalid parameter specified"}, no data).
# Top collections by 30-day volume
curl "https://api.ebisusbay.com/collections?page=1&pageSize=10"
Envelope: { status, page, totalCount, pageSize, totalPages, collections[] }.
Each collection object (key order as served):
| field | type | description |
|---|---|---|
chain | int | chain id |
verification | object | { verified: bool, doxx, kyc } |
holders | string | distinct holder count (ERC721 only; absent for ERC1155) |
totalSupply | string | token count (ERC721 only; absent for ERC1155) |
address | address | collection contract |
owner, ownerExt | string | collection owner addresses (may be blank-padded legacy strings) |
multiToken / is1155 | bool | ERC1155 flag (both keys carry the same value) |
name | string | display name |
slug | string | URL slug |
listable | bool | visible on the marketplace |
stats.total | object | active, complete, sales, floorPrice, avgSalePrice, volume, volume1d, sales1d, avgSalePrice1d, volume7d, sales7d, avgSalePrice7d, volume30d, sales30d, avgSalePrice30d — all strings, CRO-denominated; every *_increase field and royalty is always "0" |
metadata | object | description, avatar/card/banner CDN URLs, socials, optional rankings |
blacklisted | int | blacklist code (0 = clean; 4 = unreviewed default) |
Real (live) example — GET /collections?pageSize=2 (first item, trimmed):
{
"status": 200, "page": 1, "totalCount": 1090, "pageSize": 2, "totalPages": 545,
"collections": [{
"chain": 25,
"verification": { "verified": true, "doxx": "", "kyc": "" },
"holders": "687", "totalSupply": "5211",
"address": "0x719fdfb0ba006747a83438cc8900c8a2b35e0aff",
"multiToken": false, "is1155": false,
"name": "Wolfies", "slug": "wolfies", "listable": true,
"stats": { "total": {
"active": "350", "complete": "5475", "sales": "5475",
"floorPrice": "1298.000000000000000000",
"avgSalePrice": "940.272953194650817215",
"volume": "5062429.58",
"volume1d": "6785", "sales1d": "5",
"volume7d": "39835", "sales7d": "21",
"volume30d": "179598.32", "sales30d": "124",
"avgSalePrice30d": "1448.373548387096774193"
} },
"metadata": { "description": "5212 Wolfies building the future of liquid NFTs on Cronos…" },
"blacklisted": 0
}]
}
| status | body | when |
|---|---|---|
| 400 | {"status":400,"error":"Invalid parameter specified"} | any unknown query parameter (aborts) |
| 400 | {"status":400,"error":"Invalid chain specified"} | unsupported numeric chain |
| 200 (degraded) | envelope without collections | internal hydrate failure (rare) |
holders / totalSupply appear only for ERC721 collections; ERC1155 items omit both keys.*_increase fields and stats.total.royalty are always "0" (kept for shape compatibility).GET https://api.ebisusbay.com/collections?address=0x…
With an address (or its alias collection) parameter, /collections switches to the detailed lookup path (shared with the legacy /collectioninfo endpoint) and returns the same item shape as the ranked list for exactly the requested collection(s). Supports CSV addresses, plus the full legacy filter/sort surface.
| name | type | required | default | constraints |
|---|---|---|---|---|
address | address (CSV allowed) | yes* | — | collection contract(s); *either address or collection |
collection | address (CSV allowed) | yes* | — | alias for address (address wins if both given) |
chain | int or name | no | 25 | invalid numeric id → 400 |
page | int | no | 1 | |
pageSize | int | no | 100 | capped at 100 |
sortBy | enum | no | volume | one of totalvolume, totalvolume1d, totalvolume7d, totalvolume30d, totalsales1d, totalsales7d, totalsales30d, totalsales, totalactive, totalaveragesaleprice, totalfloorprice, volume, sales, floorprice, avgsaleprice, active, volume1d, sales1d, avgsaleprice1d, volume7d, sales7d, avgsaleprice7d, volume30d, sales30d, avgsaleprice30d, name (case-insensitive) |
direction | asc|desc | no | desc | exact lowercase |
verified | 1|0 | no | — | |
showHidden | 1|0 | no | — | 1 = hidden only, 0 = listable only |
blacklist | int (CSV) | no | 0,1,4 | |
owner | address (CSV) | no | — | filter by collection owner |
slug | string | no | — | filter by slug |
search | string | no | — | substring match on collection name |
supported | 1|0 | no | — | 1 = only collections with any sales or active listings |
id | — | no | — | accepted for legacy compatibility, no effect |
curl "https://api.ebisusbay.com/collections?address=0xce5cac89e25dbccd590090994919a5ef53bbd6c0"
# CSV addresses are allowed
curl "https://api.ebisusbay.com/collections?address=0xce5cac89e25dbccd590090994919a5ef53bbd6c0,0x719fdfb0ba006747a83438cc8900c8a2b35e0aff"
Same envelope and item shape as the ranked list above. For a single address, totalCount is 1.
Real (live) example — GET /collections?address=0xce5cac89e25dbccd590090994919a5ef53bbd6c0 (trimmed):
{
"status": 200, "page": 1, "totalCount": 1, "pageSize": 100, "totalPages": 1,
"collections": [{
"chain": 25,
"verification": { "verified": true, "doxx": false, "kyc": false },
"holders": "101", "totalSupply": "333",
"address": "0xce5cac89e25dbccd590090994919a5ef53bbd6c0",
"owner": "0x0dd35ff4f88ff8878488ac943fc783ea1468aa98", "ownerExt": null,
"multiToken": false, "is1155": false,
"name": "CronosEyezNFT", "slug": "cronoseyez", "listable": true,
"stats": { "total": {
"active": "2", "complete": "108", "sales": "108",
"floorPrice": "209.000000000000000000",
"avgSalePrice": "257.916666666666666666",
"volume": "27854.999999999996",
"volume30d": "0", "sales30d": "0"
} },
"metadata": { "description": "CronosEyez is a Generative Art Collection of 333 NFTs…" },
"blacklisted": 0
}]
}
| status | body | when |
|---|---|---|
| 400 | {"status":400,"error":"Invalid chain specified"} | unsupported numeric chain |
| 400 | {"status":400,"error":"Invalid parameter specified - sort must be one of …"} | bad sortBy |
| 400 | {"status":400,"error":"Invalid parameter specified - direction must be one of asc,desc"} | bad direction |
| 400 | envelope with data and "error":"Invalid parameter specified" | unknown parameter (non-aborting legacy quirk — data still computed) |
| 500 | {"status":500,"error":"Internal Server Error"} | array-typed sortBy/address/owner values |
| 200 (degraded) | envelope without collections | swallowed DB-level failures (e.g. non-numeric blacklist, page ≤ 0 with pageSize) |
collections key rather than the status."0" windowed values.verification.doxx / kyc may be booleans or strings depending on stored data ("true"/"false" strings are converted to booleans; empty strings pass through).GET https://api.ebisusbay.com/v2/wallets
Returns the NFTs currently held by a wallet on Cronos (direct on-chain holdings, ERC721 and ERC1155), enriched with collection info, token metadata, best offer and last sale. This is the wallet-inventory endpoint aggregators use to render a user's Ebisu's Bay holdings.
| name | type | required | default | constraints |
|---|---|---|---|---|
wallet | address | yes | — | wallet to inspect (lowercased) |
collection | address (CSV) | no | — | restrict to specific collections |
type | 721|1155 | no | both | token standard filter |
page | int | no | 1 | page < 1 → 500 (legacy quirk, see errors) |
pageSize | int | no | 100 | 0–100; out of range → 400 |
sortBy | enum | no | — | receivedTimestamp, id, rank, address, price, offerPrice |
direction | asc|desc | no | — | |
blacklist | int (CSV) | no | 0,1 | collection blacklist codes to include |
minRank / maxRank | int | no | — | rarity-rank bounds |
search | string/number | no | — | integer → exact token id or rank; text → token-name match; fractional number → 500 (legacy quirk) |
offered | 1|0 | no | — | 1 = only tokens with an active offer, 0 = only without |
chain | int or name | no | 25 | invalid numeric id → 400; only 25 has data |
listed | 1 | no | — | legacy parameter — currently always yields an empty result (see caveats) |
currency | address | no | — | legacy parameter — currently always yields an empty result |
minPrice/maxPrice/minListingTime/maxListingTime | number | no | — | legacy parameters — currently always yield an empty result |
curl "https://api.ebisusbay.com/v2/wallets?wallet=0x7ff3d5cc69766652495ab5c3323224cf6e2b3229&pageSize=25"
Envelope: { status, error, page, totalCount, pageSize, totalPages, nfts[] } (error is null on clean requests).
Each nfts[] item:
| field | type | description |
|---|---|---|
nftId | string | token id |
nftAddress | address | NFT contract |
chain | int | 25 |
collection | object | { name?, slug?, blacklist? } |
collectionName | string | duplicate of collection.name |
burnt | bool | true when held by the dead/zero address |
balance | string | ERC1155 only — quantity held |
owner | address | the queried wallet |
token_uri | string | resolved metadata URI (IPFS-gatewayed) |
is1155 | bool | token standard |
market | object | { listingCount? } for ERC1155 own-listings; {} otherwise |
last_sale | object | { price?, currency?, seller?, id?, uri? } for the token's last sale (ERC721 only); {} when none |
offer | object | best active offer: { id, hash, offerIndex, price, purchaser, uri }; {} when none |
| (metadata fields) | — | flattened token metadata follows: edition, name, image, original_image, animation_url, description, score, rank, attributes, … only populated keys appear |
Real (live) example — GET /v2/wallets?wallet=0x7ff3d5cc69766652495ab5c3323224cf6e2b3229&pageSize=1 (trimmed):
{
"status": 200, "error": null, "page": 1, "totalCount": 6, "pageSize": 1, "totalPages": 6,
"nfts": [{
"nftId": "808",
"nftAddress": "0x15730353272c88229a91804854078aa9b3a70448",
"chain": 25,
"collection": { "name": "CronosEyez Gen 2", "slug": "cronoseyez-gen2", "blacklist": 0 },
"collectionName": "CronosEyez Gen 2",
"burnt": false,
"owner": "0x7ff3d5cc69766652495ab5c3323224cf6e2b3229",
"token_uri": "https://ipfs.ebisusbay.com/ipfs/QmcC1dobZsMiaHiwEnvqtbrKcCfKPLsw382mmf1pAXYYRQ/808.json",
"is1155": false,
"market": {}, "last_sale": {}, "offer": {},
"edition": 808, "name": "CronosEyezGen2#808",
"image": "https://ipfs.ebisusbay.com/ipfs/Qmf2k1TqHXG5TD5Ed6E9Akx45qMyJbT2Y9J4w1wYGWCs7Y/808.png",
"rank": null,
"attributes": [{ "value": "goldGeometrik", "trait_type": "Background" }]
}]
}
| status | body | when |
|---|---|---|
| 400 | {"status":400,"error":"Must include wallet parameter"} | missing wallet |
| 400 | {"status":400,"error":"type=[1155,721]"} | bad type |
| 400 | {"status":400,"error":"Invalid chain specified"} | unsupported numeric chain |
| 400 | {"status":400,"error":"sortBy=[receivedTimestamp,id,rank,address,price,offerPrice]"} | bad sortBy |
| 400 | {"status":400,"error":"Page size parameter must be between 1 and 100"} | pageSize < 0 or > 100 |
| 400 | {"status":400,"error":"direction=[asc,desc]"} | bad direction |
| 400 | envelope with data and "error":"Invalid parameter specified - parameters must be one of …" | unknown parameter when results are non-empty (non-aborting quirk) |
| 500 | {"status":500,"error":"Database query failed","nfts":[]} | page < 1, non-numeric blacklist, fractional numeric search (legacy pg-error parity) |
| 200 | {"status":200,"error":"<stale message>","totalCount":0,"page":1,"totalPages":1,"pageSize":…,"nfts":[]} | empty result — the empty branch RESETS status to 200 but keeps any earlier validation message (legacy quirk) |
listed, currency, minPrice, maxPrice, minListingTime, maxListingTime are accepted but the underlying legacy listing-join is dead: any of them silently produces an empty result (totalCount: 0). Don't use them.sortBy=receivedTimestamp (also the implicit default) and sortBy=price have no backing data — ordering degenerates to (collection address, token id).status to 200 even if an unknown parameter was flagged; check nfts.length, not the error message.0xce3f4e59834b5b52b301e075c5b3d427b6884b3d) token ids 100–1003 are always excluded (legacy static exclusion).last_sale only ever populates for ERC721 tokens.GET https://api.ebisusbay.com/marketdata
Marketplace-wide, all-time headline totals: number of sales, active listings, traded volume and royalties, plus the marketplace (Port) contract address. Single flat JSON object — the endpoint takes no parameters.
None. (Legacy quirk: sending ANY query parameter flips the HTTP status to 400 but the same full data body is still returned.)
curl "https://api.ebisusbay.com/marketdata"
| field | type | description |
|---|---|---|
totalSales | string | all-time completed sales (listings + accepted offers) |
totalActive | string | current active listing quantity |
totalVolume | string | all-time traded volume, CRO-denominated decimal string |
totalRoyalties | string | all-time royalty sum (raw numeric string) |
contractAddress | address | the Ebisu's Bay marketplace (Port) contract on Cronos: 0x7a3cdb2364f92369a602cae81167d0679087e6a3 |
Real (live) example:
{
"totalSales": "961475",
"totalActive": "50278",
"totalVolume": "305621707.361405454999890000000000",
"totalRoyalties": "399467010154809301808023.368684639573251962",
"contractAddress": "0x7a3cdb2364f92369a602cae81167d0679087e6a3"
}
| status | body | when |
|---|---|---|
| 400 | the same full data body as 200 | any query parameter present (legacy quirk) |
| 500 | {"status":500,"error":"Internal Server Error"} | unexpected server error |
"0".totalVolume is a CRO-denominated decimal; totalRoyalties is a raw aggregate and is not directly comparable to totalVolume.GET https://api.ebisusbay.com/api/gasless-listing/validator
Rollout note: this is the drop-in replacement for the retired
GET cms.ebisusbay.com/api/gasless-listing/validator. The endpoint is implemented and will be served at the public path above once the final routing lands — until then it may still answer 404 on the live host. The examples below are constructed from the implementation and its test fixtures, not live captures.
To purchase listings you need a short-lived, server-signed Approval. This endpoint validates the requested listings (they must all still be active), reconstructs the exact TradeShip Order tuples, computes the marketplace fee, and returns everything you pass to the on-chain fillOrders call — including the server signature that the contract verifies. It accepts both gasless listings (32-byte 0x… digest ids) and legacy Port listings (decimal ids), mixed freely in one request.
| name | type | required | default | constraints / encodings |
|---|---|---|---|---|
address | address | yes | — | the buyer (pays and, unless recipient is given, must send the fill transaction). Must be a valid EVM address; not the zero address; not the marketplace contracts. |
listingIds | string list | yes | — | 1–100 listing ids. All of these encodings are accepted: legacy array keys listingIds[]=a&listingIds[]=b (also percent-encoded listingIds%5B%5D=), repeated plain keys listingIds=a&listingIds=b, and comma-separated listingIds=a,b. Ids are trimmed; empties dropped; duplicates de-duplicated; each id ≤ 78 chars; 0x… ids are case-insensitive (normalized to lowercase). |
recipient | address | no | = address | receives the NFTs AND becomes the on-chain filler — when set, the fill transaction must be sent from recipient, so it must be an address you control. Not zero, not the marketplace contracts. |
executor | string | no | — | legacy parameter. transak (any case) → HTTP 400 (the Transak flow has been discontinued). Any other value is ignored. |
merchantsSeal | string | no | — | legacy parameter, accepted and ignored — the fee is server-pinned; membership-tier fee discounts no longer apply. |
There is no chainId parameter: this endpoint is pinned to Cronos mainnet (chain 25), exactly like the legacy API.
The payload is wrapped in a data envelope. Field set is exactly:
| field | type | description |
|---|---|---|
data.expire | int (unix s) | Approval expiry — 900 seconds (15 minutes) from issuance. The contract rejects the signature after this. |
data.feeAmount | string (wei) | marketplace fee, flat 350 bps (3.5%) of the total order value expressed in native CRO. ERC20-priced listings are converted to a CRO-equivalent server-side before the fee is computed. Paid in native CRO on top of the prices. |
data.feeToken | address | always 0x0000000000000000000000000000000000000000 (fee charged in native CRO) |
data.filler | address (lowercase) | the address fillOrders must be called from (recipient if provided, else address) |
data.sigs | string | per-order maker signatures, index-aligned with orderData. Gasless listings carry the seller's 65-byte EIP-712 signature; legacy Port listings carry the 39-zero-byte sentinel 0x000000000000000000000000000000000000000000000000000000000000000000000000000000. Pass through verbatim. |
data.signature | string | the server's EIP-712 signature over {expire, feeAmount, feeToken, filler, sigs} — the _serverSig argument of fillOrders |
data.orderData | Order | the TradeShip Order tuples to pass as _orders (see shape below) |
Order shape (each element of orderData):
| field | type | description |
|---|---|---|
offerer | address | the seller |
offerings | OfferItem1 | what the seller gives. itemType 1 = ERC721, 2 = ERC1155 (gasless), 6 = LEGACY_LISTING (legacy Port rows — identifierOrCriteria is then the listing id, not the token id) |
considerations | OfferItem1 | what the buyer pays. itemType 0 = NATIVE (token = zero address) or 3 = ERC20 (token = currency); startAmount/endAmount = price in wei |
orderType | int | 0 = SELL_NFT_NATIVE, 2 = SELL_NFT_TOKEN |
startAt / endAt | int (unix s) | order validity window (legacy no-expiry listings use the far-future constant 9995868693) |
salt | string | uint256 salt (string to preserve precision) |
OfferItem = { itemType, token, identifierOrCriteria, startAmount, endAmount }.
Realistic example (constructed from the implementation's parity-test fixtures — one gasless listing at 100 CRO + one legacy listing at 50 CRO; fee = 3.5% of 150 CRO = 5.25 CRO):
GET /api/gasless-listing/validator?address=0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
&listingIds[]=0xabababababababababababababababababababababababababababababababab
&listingIds[]=12345
{
"data": {
"expire": 1751698638,
"feeAmount": "5250000000000000000",
"feeToken": "0x0000000000000000000000000000000000000000",
"filler": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"sigs": [
"0x1111…(65-byte seller signature)…11",
"0x000000000000000000000000000000000000000000000000000000000000000000000000000000"
],
"signature": "0x2222…(65-byte server signature)…22",
"orderData": [
{
"offerer": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"offerings": [{
"itemType": 1,
"token": "0xcccccccccccccccccccccccccccccccccccccccc",
"identifierOrCriteria": "1001",
"startAmount": "1", "endAmount": "1"
}],
"considerations": [{
"itemType": 0,
"token": "0x0000000000000000000000000000000000000000",
"identifierOrCriteria": "0",
"startAmount": "100000000000000000000", "endAmount": "100000000000000000000"
}],
"orderType": 0,
"startAt": 1751696738, "endAt": 1751784138,
"salt": "7"
},
{
"offerer": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"offerings": [{
"itemType": 6,
"token": "0xcccccccccccccccccccccccccccccccccccccccc",
"identifierOrCriteria": "12345",
"startAmount": "1", "endAmount": "1"
}],
"considerations": [{
"itemType": 0,
"token": "0x0000000000000000000000000000000000000000",
"identifierOrCriteria": "0",
"startAmount": "50000000000000000000", "endAmount": "50000000000000000000"
}],
"orderType": 0,
"startAt": 1751696738, "endAt": 9995868693,
"salt": "0"
}
]
}
}
Every response carries cache-control: no-store (success AND error) — never cache it.
Errors are a flat {"error": "<string>"} body (no data envelope, no status field, no structured code).
| status | error message(s) | when |
|---|---|---|
| 400 | Missing or invalid address. | address missing / malformed |
| 400 | Missing listingIds. / Too many listingIds (max 100). / listingIds entries must be non-empty strings. | listing id set malformed |
| 400 | Invalid recipient. / recipient cannot be the zero address / recipient cannot be the marketplace contract | bad recipient |
| 400 | buyer cannot be the zero address / buyer cannot be the marketplace contract | bad address value |
| 400 | The transak executor is no longer supported. | executor=transak |
| 403 | N listing(s) are no longer active | some requested ids resolved but others are sold / cancelled / expired / unknown — the server refuses to sign a partial Approval |
| 403 | order has expired / order has not started yet | a resolved listing's time bounds fail at signing time |
| 404 | no active listings found for the requested ids | none of the requested ids resolve to an active listing |
| 500 | {"error": "<message>"} or generic 500 | corrupt listing row (e.g. malformed id), internal failure |
msg.value inputs from the response's feeAmount + orderData.expire field). If you haven't filled by then, re-fetch — the contract rejects expired approvals. Responses are cache-control: no-store; do not cache or reuse them across purchases.{error: string} with meaningful HTTP status codes (400/403/404/500). There is no error code field.fillOrders revert. Simulate (eth_call/estimateGas) before broadcasting and be prepared to refresh the listing set and re-validate.merchantsSeal is accepted but ignored — legacy membership/VIP fee discounts have been removed; there is no way to lower the fee.executor=transak returns 400. Other executor values are ignored (legacy behavior).recipient is set, it becomes the filler — the fill transaction must be sent from the recipient address, or the contract rejects the Approval.sigs blob lengths differ by design (65-byte real signatures vs the 39-byte legacy sentinel) — pass them through untouched; re-encoding or normalizing them breaks the on-chain Approval decoding.Step 1 — find listings. GET /listings?state=0&collection=0x…&pageSize=… (see GET /listings). Collect listingId values (and note each listing's currency).
Step 2 — validate. GET /api/gasless-listing/validator?address=<buyer>&listingIds[]=<id1>&listingIds[]=<id2>. Keep the whole data object; you will pass its parts to the contract unmodified.
Step 3 — compute payment. The fee is always native; each order's price is in its consideration:
msg.value = feeAmount
+ Σ orderData[i].considerations[0].startAmount
for every i where considerations[0].itemType == 0 // NATIVE
For ERC20-priced orders (considerations[0].itemType == 3), instead approve the TradeShip contract for startAmount of considerations[0].token before filling (the price is pulled via transferFrom; only the fee stays in msg.value).
Step 4 — fill on-chain. Call the TradeShip ("ship") contract on Cronos mainnet:
0x523d6f30c4aaca133daad97ee2a0c48235bff137
Function (payable):
function fillOrders(
TradeshipCrates.Order[] calldata _orders, // = data.orderData
TradeshipCrates.Approval calldata _approval, // = { expire, feeAmount, feeToken, filler, sigs }
bytes calldata _serverSig // = data.signature
) external payable;
struct Order {
address offerer;
OfferItem[] offerings;
OfferItem[] considerations;
uint8 orderType;
uint256 startAt;
uint256 endAt;
uint256 salt;
}
struct OfferItem {
uint8 itemType;
address token;
uint256 identifierOrCriteria;
uint256 startAmount;
uint256 endAmount;
}
struct Approval {
uint256 expire;
uint256 feeAmount;
address feeToken;
address filler;
bytes[] sigs;
}
The transaction must be sent from data.filler (the contract checks msg.sender == filler), and must land before data.expire.
Step 5 — handle failure. If the transaction reverts (commonly a stale listing), refetch the listing (GET /listings?listingId=…), drop ids whose state != 0 or valid != true, and repeat from Step 2. If the Approval aged past 900 s, just repeat Step 2.
# Validate two listings for a buyer (-g keeps the [] brackets literal)
curl -g "https://api.ebisusbay.com/api/gasless-listing/validator?address=0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&listingIds[]=0xabababababababababababababababababababababababababababababababab&listingIds[]=12345"
# Comma-separated ids work too
curl "https://api.ebisusbay.com/api/gasless-listing/validator?address=0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb&listingIds=0xabababababababababababababababababababababababababababababababab,12345"
This content is for informational purposes only and does not constitute financial, investment, or legal advice. Blockchain technology and decentralized finance involve significant risks, including the potential loss of all funds. All interactions with Ebisu's Bay and its affiliated services — including trading NFTs, swapping, bridging, and any other on-chain activities — are performed entirely at your own risk. You are solely responsible for your own decisions and due diligence. Ebisu's Bay makes no guarantees regarding the performance, security, or value of any tokens, NFTs, or DeFi protocols.