Ebisu's Bay API — Developer Reference

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.


Getting Started

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.

Deprecated legacy endpoints

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 /listings

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.

Request parameters

nametyperequireddefaultconstraints / notes
listingIdstring (CSV allowed)noexact listing id(s); comma-separate for multiple
collectionaddress (CSV allowed)nofilter by NFT contract address(es), case-insensitive
tokenIdstring (CSV allowed)nofilter by token id within a collection
selleraddressnolowercased before matching
purchaseraddressnolowercased before matching
stateint 0|1|2no0 = active, 1 = sold, 2 = cancelled; other values → 400
currencyaddress (CSV allowed)nopayment token; 0x000…0 = native CRO
chainint or nameno25one of 1,25,338,282,388 or ethereum,cronos,cronos-testnet,cronos-zk-testnet,cronos-zk; unsupported numeric id → 400. Only 25 returns data.
pageintno11-based
pageSizeintno100capped at 100
sortByenumnolistingTimelistingId, listingTime, saleTime, price, rank, expirationDate, amount, pricePerItem. price/pricePerItem sort by USD value. expirationDate, amount and pricePerItem require direction, otherwise ordering is unspecified.
directionenumnodesc (when sortBy given)asc | desc (exact, lowercase)
verified1|0no1 = only verified collections, 0 = only unverified
showHidden1|0no1 = only unlistable (hidden) collections, 0 = only listable
blacklistint (CSV allowed)no0,1,4collection blacklist codes to include
minPrice / maxPricenumbernoinclusive bounds on the listing price (listing currency units)
minListingTime / maxListingTimeunix secondsnoinclusive bounds
minSaleTime / maxSaleTimeunix secondsnoinclusive bounds
minRank / maxRankintnoinclusive rarity-rank bounds
searchstring/numbernonumeric values match token id or exact rank; free-text search is currently non-functional (see caveats)
invalidnoaccepted for legacy compatibility; has no effect
traits / powertraitsJSON objectnoaccepted for legacy compatibility; currently non-functional on this endpoint — for working trait filtering use /fullcollections. Malformed JSON → 500

Example

# 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"

Response

Envelope (key order as served):

fieldtypedescription
statusintmirrors the HTTP status (200 on success)
errorstringpresent only when a validation problem occurred
pageintechoed page
totalCountinttotal matching rows
pageSizeinteffective page size (≤100)
totalPagesintceil(totalCount / pageSize)
listingsarraythe page of listing objects

Each listing object:

fieldtypedescription
listingIdstringlisting id — decimal for legacy Port listings, 0x… 32-byte digest for gasless listings; feed this to the validator
nftIdstringtoken id
nftAddressaddressNFT contract
chainintchain id (25)
validboolfalse when the listing was flagged invalid (invalid > 1)
amountstringquantity listed (ERC1155 can be > 1)
invalidintraw invalidity flag (0/1 pass the default filter)
selleraddresslister
purchaseraddress|nullbuyer once sold (zero address until then)
stateint0 active / 1 sold / 2 cancelled
pricestringhuman-decimal price in currency units (NOT wei)
currencyaddresspayment token; 0x000…0 = native CRO
royaltystringroyalty amount
feestringmarketplace fee amount
saleTimestringunix seconds of sale as string; "0" when unsold
listingTimestringunix seconds the listing was created
expirationDatestring|nullunix seconds, or null for no expiry (legacy)
transactionHashstring|nulllisting tx hash if known
nftobjecttoken metadata: nftId, nftAddress, chain, then metadata fields (edition, name, image, original_image, animation_url, description, score, rank, attributes, …) — only populated keys appear
collectionobject{ 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" }
    }
  }]
}

Errors

statusbodywhen
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
400full 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 paginationinputs 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

Caveats

  • Unknown-parameter quirk: an unknown parameter yields HTTP 400 but the body still contains the full result page (verified live). Do not treat HTTP 400 as "no data" — check for the listings key.
  • Free-text 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).
  • Only chain 25 (Cronos) returns rows; chain=388 validates but yields totalCount: 0.
  • An unknown chain name (e.g. chain=foo) is not rejected — it silently disables the chain filter (legacy quirk).
  • Default feed excludes: listings with invalid > 1, expired-and-still-active listings, and collections outside blacklist codes {0, 1, 4}.
  • Results may be up to ~60 s stale (server-side response caching on hot query shapes).
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /fullcollections

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.

Request parameters

nametyperequireddefaultconstraints / notes
addressaddress (CSV allowed)yesNFT contract address(es), case-insensitive
traitsJSON objectnotrait filter, e.g. {"Background":["Pink","Blue"],"Fur":["Gold"]} — see Trait filtering below
idsOnly1|truenooffreturn only the full matching token-id list (tokenIds) instead of the paginated nfts page; page/pageSize/sortBy/direction are ignored
tokenstring (CSV allowed)norestrict to specific token id(s)
listed1|0no1 = only tokens with an active listing, 0 = only unlisted tokens
currencyaddress (CSV allowed)nokeep only tokens whose active listing uses this payment token; 0x000…0 = native CRO
minPrice / maxPricenumbernoinclusive bounds on the active listing price (listing currency units); tokens without an active listing are dropped
minListingTime / maxListingTimeunix secondsnoinclusive bounds on the active listing's creation time
minRank / maxRankintnoinclusive rarity-rank bounds
searchstring/numbernonumeric = token id or exact rank; text = substring match over name/attributes
burnt1|0no1 = only burnt tokens, 0 = only non-burnt
chainint or nameno25same values as /listings; only chain 25 returns data
blacklistint (CSV allowed)no0,1,4collection blacklist codes to include
pageintno11-based
pageSizeintno100capped at 100
sortByenumnoidid, name, rank, price, listingId, listingTime, expirationDate
directionenumnoascasc | desc (exact, lowercase)
powertraitsJSON objectnoaccepted for legacy compatibility; currently non-functional. Malformed JSON → 500

Trait filtering

traits is a JSON object mapping trait categories to arrays of accepted values, URL-encoded into the query string:

  • AND across categories, OR within a category{"Background":["Pink","Blue"],"Fur":["Gold"]} matches tokens whose Background is Pink or Blue and whose Fur is Gold.
  • Values match exactly (case-sensitive): "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).
  • Numeric attribute values match whether the collection's metadata stored them as strings or numbers — pass them as strings ({"Level":["5"]}).
  • Limits: at most 32 categories, 64 values per category, 256 characters per name/value — beyond that the request is rejected with a clean 400.
  • Malformed traits JSON (or a non-string value item, e.g. [5] without quotes) → 500 (legacy behavior; fix the JSON).

Example — all token ids with a trait

# 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"

Response

With idsOnly=1:

fieldtypedescription
statusint200 on success
totalCountintnumber of matching tokens
tokenIdsstringall matching token ids, sorted numerically ascending

Without idsOnly (default), the paginated envelope:

fieldtypedescription
statusint200 on success
page / totalCount / pageSize / totalPagesintpagination (pageSize ≤ 100)
nftsarraythe 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" }]
  }]
}

Errors

statusbodywhen
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} onlyinputs the legacy backend treated as a swallowed DB error (e.g. non-numeric blacklist, fractional minRank)

Caveats

  • 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).
  • Results may be up to ~60 s stale (server-side response caching); token-id sets for a fixed trait filter change rarely, so this is usually irrelevant.
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /collections — ranked list

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.

Request parameters (ranked mode)

nametyperequireddefaultconstraints
pageintno1min 1
pageSizeintno50clamped to 1, 100
chainint or nameno25invalid numeric id → 400

Unlike most legacy endpoints, the ranked list rejects unknown parameters outright (HTTP 400, {"status":400,"error":"Invalid parameter specified"}, no data).

Example

# Top collections by 30-day volume
curl "https://api.ebisusbay.com/collections?page=1&pageSize=10"

Response

Envelope: { status, page, totalCount, pageSize, totalPages, collections[] }.

Each collection object (key order as served):

fieldtypedescription
chainintchain id
verificationobject{ verified: bool, doxx, kyc }
holdersstringdistinct holder count (ERC721 only; absent for ERC1155)
totalSupplystringtoken count (ERC721 only; absent for ERC1155)
addressaddresscollection contract
owner, ownerExtstringcollection owner addresses (may be blank-padded legacy strings)
multiToken / is1155boolERC1155 flag (both keys carry the same value)
namestringdisplay name
slugstringURL slug
listableboolvisible on the marketplace
stats.totalobjectactive, 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"
metadataobjectdescription, avatar/card/banner CDN URLs, socials, optional rankings
blacklistedintblacklist 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
  }]
}

Errors

statusbodywhen
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 collectionsinternal hydrate failure (rare)

Caveats

  • Ranking is by 30-day CRO volume, refreshed on a ~5-minute server-side cache; positions can lag trades by a few minutes.
  • Volumes/floors are CRO-denominated decimal strings.
  • 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).
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /collections?address= — single-collection detail

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.

Request parameters (detail mode; names are case-insensitive)

nametyperequireddefaultconstraints
addressaddress (CSV allowed)yes*collection contract(s); *either address or collection
collectionaddress (CSV allowed)yes*alias for address (address wins if both given)
chainint or nameno25invalid numeric id → 400
pageintno1
pageSizeintno100capped at 100
sortByenumnovolumeone 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)
directionasc|descnodescexact lowercase
verified1|0no
showHidden1|0no1 = hidden only, 0 = listable only
blacklistint (CSV)no0,1,4
owneraddress (CSV)nofilter by collection owner
slugstringnofilter by slug
searchstringnosubstring match on collection name
supported1|0no1 = only collections with any sales or active listings
idnoaccepted for legacy compatibility, no effect

Example

curl "https://api.ebisusbay.com/collections?address=0xce5cac89e25dbccd590090994919a5ef53bbd6c0"

# CSV addresses are allowed
curl "https://api.ebisusbay.com/collections?address=0xce5cac89e25dbccd590090994919a5ef53bbd6c0,0x719fdfb0ba006747a83438cc8900c8a2b35e0aff"

Response

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
  }]
}

Errors

statusbodywhen
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
400envelope 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 collectionsswallowed DB-level failures (e.g. non-numeric blacklist, page ≤ 0 with pageSize)

Caveats

  • Detail-mode unknown parameters DON'T abort — you get HTTP 400 with data (opposite of the ranked list, which aborts). Check for the collections key rather than the status.
  • Stats come from the same ~5-minute live stats cache as the ranked list; a collection with no recent activity legitimately shows "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).
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /v2/wallets

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.

Request parameters (names case-insensitive)

nametyperequireddefaultconstraints
walletaddressyeswallet to inspect (lowercased)
collectionaddress (CSV)norestrict to specific collections
type721|1155nobothtoken standard filter
pageintno1page < 1 → 500 (legacy quirk, see errors)
pageSizeintno1000–100; out of range → 400
sortByenumnoreceivedTimestamp, id, rank, address, price, offerPrice
directionasc|descno
blacklistint (CSV)no0,1collection blacklist codes to include
minRank / maxRankintnorarity-rank bounds
searchstring/numbernointeger → exact token id or rank; text → token-name match; fractional number → 500 (legacy quirk)
offered1|0no1 = only tokens with an active offer, 0 = only without
chainint or nameno25invalid numeric id → 400; only 25 has data
listed1nolegacy parameter — currently always yields an empty result (see caveats)
currencyaddressnolegacy parameter — currently always yields an empty result
minPrice/maxPrice/minListingTime/maxListingTimenumbernolegacy parameters — currently always yield an empty result

Example

curl "https://api.ebisusbay.com/v2/wallets?wallet=0x7ff3d5cc69766652495ab5c3323224cf6e2b3229&pageSize=25"

Response

Envelope: { status, error, page, totalCount, pageSize, totalPages, nfts[] } (error is null on clean requests).

Each nfts[] item:

fieldtypedescription
nftIdstringtoken id
nftAddressaddressNFT contract
chainint25
collectionobject{ name?, slug?, blacklist? }
collectionNamestringduplicate of collection.name
burntbooltrue when held by the dead/zero address
balancestringERC1155 only — quantity held
owneraddressthe queried wallet
token_uristringresolved metadata URI (IPFS-gatewayed)
is1155booltoken standard
marketobject{ listingCount? } for ERC1155 own-listings; {} otherwise
last_saleobject{ price?, currency?, seller?, id?, uri? } for the token's last sale (ERC721 only); {} when none
offerobjectbest 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" }]
  }]
}

Errors

statusbodywhen
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
400envelope 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)

Caveats

  • Holdings are direct on-chain ownership only — tokens deposited into staking contracts are not attributed to the depositor on this endpoint.
  • 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).
  • Empty results reset status to 200 even if an unknown parameter was flagged; check nfts.length, not the error message.
  • Ryoshi Resources (0xce3f4e59834b5b52b301e075c5b3d427b6884b3d) token ids 100–1003 are always excluded (legacy static exclusion).
  • last_sale only ever populates for ERC721 tokens.
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /marketdata

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.

Request parameters

None. (Legacy quirk: sending ANY query parameter flips the HTTP status to 400 but the same full data body is still returned.)

Example

curl "https://api.ebisusbay.com/marketdata"

Response

fieldtypedescription
totalSalesstringall-time completed sales (listings + accepted offers)
totalActivestringcurrent active listing quantity
totalVolumestringall-time traded volume, CRO-denominated decimal string
totalRoyaltiesstringall-time royalty sum (raw numeric string)
contractAddressaddressthe Ebisu's Bay marketplace (Port) contract on Cronos: 0x7a3cdb2364f92369a602cae81167d0679087e6a3

Real (live) example:

{
  "totalSales": "961475",
  "totalActive": "50278",
  "totalVolume": "305621707.361405454999890000000000",
  "totalRoyalties": "399467010154809301808023.368684639573251962",
  "contractAddress": "0x7a3cdb2364f92369a602cae81167d0679087e6a3"
}

Errors

statusbodywhen
400the same full data body as 200any query parameter present (legacy quirk)
500{"status":500,"error":"Internal Server Error"}unexpected server error

Caveats

  • Figures are rematerialized server-side roughly every 5 minutes; immediately after a process restart a cold worker can briefly serve zeroed values (observed live) — retry once if all fields are "0".
  • totalVolume is a CRO-denominated decimal; totalRoyalties is a raw aggregate and is not directly comparable to totalVolume.
  • Requests may be throttled at the edge; handle 429/503 with backoff.

GET /api/gasless-listing/validator

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.

Request parameters (query string)

nametyperequireddefaultconstraints / encodings
addressaddressyesthe 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.
listingIdsstring listyes1–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).
recipientaddressno= addressreceives 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.
executorstringnolegacy parameter. transak (any case) → HTTP 400 (the Transak flow has been discontinued). Any other value is ignored.
merchantsSealstringnolegacy 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.

Response (HTTP 200)

The payload is wrapped in a data envelope. Field set is exactly:

fieldtypedescription
data.expireint (unix s)Approval expiry — 900 seconds (15 minutes) from issuance. The contract rejects the signature after this.
data.feeAmountstring (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.feeTokenaddressalways 0x0000000000000000000000000000000000000000 (fee charged in native CRO)
data.filleraddress (lowercase)the address fillOrders must be called from (recipient if provided, else address)
data.sigsstringper-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.signaturestringthe server's EIP-712 signature over {expire, feeAmount, feeToken, filler, sigs} — the _serverSig argument of fillOrders
data.orderDataOrderthe TradeShip Order tuples to pass as _orders (see shape below)

Order shape (each element of orderData):

fieldtypedescription
offereraddressthe seller
offeringsOfferItem1what 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)
considerationsOfferItem1what the buyer pays. itemType 0 = NATIVE (token = zero address) or 3 = ERC20 (token = currency); startAmount/endAmount = price in wei
orderTypeint0 = SELL_NFT_NATIVE, 2 = SELL_NFT_TOKEN
startAt / endAtint (unix s)order validity window (legacy no-expiry listings use the far-future constant 9995868693)
saltstringuint256 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

Errors are a flat {"error": "<string>"} body (no data envelope, no status field, no structured code).

statuserror message(s)when
400Missing or invalid address.address missing / malformed
400Missing listingIds. / Too many listingIds (max 100). / listingIds entries must be non-empty strings.listing id set malformed
400Invalid recipient. / recipient cannot be the zero address / recipient cannot be the marketplace contractbad recipient
400buyer cannot be the zero address / buyer cannot be the marketplace contractbad address value
400The transak executor is no longer supported.executor=transak
403N listing(s) are no longer activesome requested ids resolved but others are sold / cancelled / expired / unknown — the server refuses to sign a partial Approval
403order has expired / order has not started yeta resolved listing's time bounds fail at signing time
404no active listings found for the requested idsnone of the requested ids resolve to an active listing
500{"error": "<message>"} or generic 500corrupt listing row (e.g. malformed id), internal failure

Caveats

  • The fee is a flat 350 bps (3.5%) and server-pinned. You cannot influence it, and you should NEVER recompute it client-side — ERC20-priced listings are converted into the native fee basis using server-side price data you can't reproduce exactly. Always take msg.value inputs from the response's feeAmount + orderData.
  • The Approval expires 900 s (15 min) after issuance (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.
  • Cronos mainnet only (chain 25). There is no chainId parameter; zkEVM/testnets are not served by this endpoint.
  • Errors are flat {error: string} with meaningful HTTP status codes (400/403/404/500). There is no error code field.
  • No on-chain pre-validation is performed on the seller side (ownership, token approval): a listing that went stale between validation and fill (seller transferred or delisted the NFT, ERC1155 balance dropped, etc.) will make fillOrders revert. Simulate (eth_call/estimateGas) before broadcasting and be prepared to refresh the listing set and re-validate.
  • All-or-nothing: if any requested id is inactive the whole request fails with 403 — drop the dead ids and re-request rather than retrying the same set.
  • merchantsSeal is accepted but ignored — legacy membership/VIP fee discounts have been removed; there is no way to lower the fee.
  • The Transak executor flow was removed: executor=transak returns 400. Other executor values are ignored (legacy behavior).
  • When recipient is set, it becomes the filler — the fill transaction must be sent from the recipient address, or the contract rejects the Approval.
  • Requests may be throttled at the edge; handle 429/503 with backoff.
  • 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.

Fill flow: browse → validate → fillOrders

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.

Example — validate and fill

# 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"

Disclaimer

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.