OpenSea Collections
NFT collection rankings on OpenSea across every chain — floor price, volume, supply, owners, socials and holders.
1,000 rows in the dataset
Download the dataset
Get every row this scraper has collected as one CSV or JSONL file. If the data is older than maxAgeHours, the scraper is re-run with its configured settings first. The key needs the datasets:export scope.
DOWNLOAD_ID=$(curl -s -X POST "https://api.pandorlabs.com/v1/datasets/opensea-collections/downloads" \ -H "Authorization: Bearer $PANDORLABS_KEY" \ -H "Content-Type: application/json" \ -d '{"maxAgeHours":24,"format":"csv"}' | jq -r '.id')while true; do STATUS=$(curl -s "https://api.pandorlabs.com/v1/downloads/$DOWNLOAD_ID" \ -H "Authorization: Bearer $PANDORLABS_KEY" | jq -r '.status') [ "$STATUS" != "refreshing" ] && break sleep 5donecurl -s "https://api.pandorlabs.com/v1/downloads/$DOWNLOAD_ID/file" \ -H "Authorization: Bearer $PANDORLABS_KEY" \ -o dataset.csvAt its current 1,000 rows, this download costs 10 credits when the data is fresh enough, or 20 credits when it has to be refreshed first.
The rate is 1 credit per 100 rows cached and 2 credits live, charged on the rows in the dataset when the download becomes ready. A failed refresh is not charged. The file stays downloadable for 24 hours. See pricing.
Start a run
To choose the targets yourself, send the parameters below as input. The call returns 202 with the run; the key needs the scrapers:run scope and the account a positive credit balance.
curl -X POST "https://api.pandorlabs.com/v1/scrapers/opensea-collections/runs" \ -H "Authorization: Bearer $PANDORLABS_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "sortBy": "VOLUME", "sortDirection": "DESC", "chains": [ "ethereum" ], "category": "PFPS" } }'A run is billed at the live rate once it finishes: 2 credits per 100 rows it collected. A run that collects nothing costs nothing.
Parameters
Every parameter is optional. Anything you leave out uses the default shown here; an unknown name or an invalid value is rejected with a 400 that lists each problem.
Targeting
sortByenum
Rank by
Which volume window ranks the table. Only the volume family is offered — OpenSea accepts FLOOR_PRICE and SALES and then drops the connection.
- Default
VOLUME- Allowed values
VOLUMEONE_MINUTE_VOLUMEFIVE_MINUTE_VOLUMEFIFTEEN_MINUTE_VOLUMEONE_HOUR_VOLUMEONE_DAY_VOLUMESEVEN_DAYS_VOLUMETHIRTY_DAYS_VOLUME
sortDirectionenum
Direction
- Default
DESC- Allowed values
DESCASC
chainsstring[]
Chains
Chain slugs — ethereum, solana, base, polygon, hyperevm. Empty collects every chain. An unknown slug fails the whole page, so check spelling.
- Default
- none
- Example
ethereum
categorystring
Category
OpenSea's own taxonomy, upper-cased: PFPS, ART, GAMING, … An unrecognised value fails the page rather than being ignored.
- Default
- none
- Example
PFPS
ownerAddressstring
Owned by wallet
Collect only the collections this wallet owns. OpenSea's own filter, so it is exact rather than a search — a wallet nobody matches returns an empty first page, which is warned about.
- Default
- none
- Example
0x…
maxCollectionsnumber
Max collections per run
Ceiling on rows collected. Paging was walked past 25,000 collections without a duplicate or a refusal, so this bounds the run rather than the endpoint. Rows upsert, so it bounds one run rather than the dataset, and a ceiling that truncates the listing is reported as a warning.
- Default
1000- Range
- 1 to 50000
Holders
enrichHoldersboolean
Sample holders
Walk each collection’s tokens and record the wallets behind them, with their OpenSea portfolio value and X handle. Costs one request per 100 tokens.
- Default
false
holderCollectionsstring[]
Sample only these slugs
Restrict holder sampling to named collections. Empty samples the highest-ranked ones this run collected.
- Default
- none
- Example
cryptopunks
maxHolderCollectionsnumber
Max collections sampled
- Default
10- Range
- 1 to 500
holderItemsPerCollectionnumber
Tokens walked per collection
A sample, not a census: 6,000 tokens of BAYC yielded 3,661 distinct wallets. One request per 100 tokens, and paging held for every one of those 60 pages.
- Default
500- Range
- 1 to 20000
topHoldersKeptnumber
Top holders stored per collection
Richest first. The counts and the median are computed over the whole sample regardless of this.
- Default
50- Range
- 1 to 1000
holderValueSourceenum
Portfolio valuation from
opensea is free and arrives inline. nftscan needs NFTSCAN_API_KEY and costs one request per wallet; without a key the run fails rather than silently downgrading.
- Default
opensea- Allowed values
openseanftscan
nftscanApiKeystring
NFTScan API key
Overrides the NFTSCAN_API_KEY environment variable. Only read when the valuation source is nftscan.
- Default
- none
Advanced
collectionsPerPagenumber
Collections per request
OpenSea rejects anything above 100.
- Default
100- Range
- 1 to 100
requestDelayMsnumber
Delay between requests (ms)
The endpoint reports a 400-request budget in its own response extensions; 500 ms keeps a long run comfortably inside it. 252 consecutive pages at 400 ms drew no refusal on 2026-09-03.
- Default
500- Range
- 0 to 30000
Input as JSON Schema
The same schema the API validates against. Use it as a tool input schema or to generate a typed client.
{ "type": "object", "additionalProperties": false, "properties": { "sortBy": { "type": "string", "enum": [ "VOLUME", "ONE_MINUTE_VOLUME", "FIVE_MINUTE_VOLUME", "FIFTEEN_MINUTE_VOLUME", "ONE_HOUR_VOLUME", "ONE_DAY_VOLUME", "SEVEN_DAYS_VOLUME", "THIRTY_DAYS_VOLUME" ], "title": "Rank by", "description": "Which volume window ranks the table. Only the volume family is offered — OpenSea accepts FLOOR_PRICE and SALES and then drops the connection.", "default": "VOLUME" }, "sortDirection": { "type": "string", "enum": [ "DESC", "ASC" ], "title": "Direction", "default": "DESC" }, "chains": { "type": "array", "items": { "type": "string" }, "maxItems": 1000, "title": "Chains", "description": "Chain slugs — ethereum, solana, base, polygon, hyperevm. Empty collects every chain. An unknown slug fails the whole page, so check spelling.", "default": [], "examples": [ "ethereum" ] }, "category": { "type": "string", "maxLength": 2000, "title": "Category", "description": "OpenSea's own taxonomy, upper-cased: PFPS, ART, GAMING, … An unrecognised value fails the page rather than being ignored.", "examples": [ "PFPS" ] }, "ownerAddress": { "type": "string", "maxLength": 2000, "title": "Owned by wallet", "description": "Collect only the collections this wallet owns. OpenSea's own filter, so it is exact rather than a search — a wallet nobody matches returns an empty first page, which is warned about.", "examples": [ "0x…" ] }, "maxCollections": { "type": "number", "minimum": 1, "maximum": 50000, "title": "Max collections per run", "description": "Ceiling on rows collected. Paging was walked past 25,000 collections without a duplicate or a refusal, so this bounds the run rather than the endpoint. Rows upsert, so it bounds one run rather than the dataset, and a ceiling that truncates the listing is reported as a warning.", "default": 1000 }, "enrichHolders": { "type": "boolean", "title": "Sample holders", "description": "Walk each collection’s tokens and record the wallets behind them, with their OpenSea portfolio value and X handle. Costs one request per 100 tokens.", "default": false }, "holderCollections": { "type": "array", "items": { "type": "string" }, "maxItems": 1000, "title": "Sample only these slugs", "description": "Restrict holder sampling to named collections. Empty samples the highest-ranked ones this run collected.", "default": [], "examples": [ "cryptopunks" ] }, "maxHolderCollections": { "type": "number", "minimum": 1, "maximum": 500, "title": "Max collections sampled", "default": 10 }, "holderItemsPerCollection": { "type": "number", "minimum": 1, "maximum": 20000, "title": "Tokens walked per collection", "description": "A sample, not a census: 6,000 tokens of BAYC yielded 3,661 distinct wallets. One request per 100 tokens, and paging held for every one of those 60 pages.", "default": 500 }, "topHoldersKept": { "type": "number", "minimum": 1, "maximum": 1000, "title": "Top holders stored per collection", "description": "Richest first. The counts and the median are computed over the whole sample regardless of this.", "default": 50 }, "holderValueSource": { "type": "string", "enum": [ "opensea", "nftscan" ], "title": "Portfolio valuation from", "description": "opensea is free and arrives inline. nftscan needs NFTSCAN_API_KEY and costs one request per wallet; without a key the run fails rather than silently downgrading.", "default": "opensea" }, "nftscanApiKey": { "type": "string", "maxLength": 2000, "title": "NFTScan API key", "description": "Overrides the NFTSCAN_API_KEY environment variable. Only read when the valuation source is nftscan." }, "collectionsPerPage": { "type": "number", "minimum": 1, "maximum": 100, "title": "Collections per request", "description": "OpenSea rejects anything above 100.", "default": 100 }, "requestDelayMs": { "type": "number", "minimum": 0, "maximum": 30000, "title": "Delay between requests (ms)", "description": "The endpoint reports a 400-request budget in its own response extensions; 500 ms keeps a long run comfortably inside it. 252 consecutive pages at 400 ms drew no refusal on 2026-09-03.", "default": 500 } }}Run, poll, read rows
The run is asynchronous. Poll it until it reaches a terminal status and its billing is paid, then page through its rows. Until then the rows endpoint answers 409 (still queued or running) or 402 (payment required).
RUN_ID=$(curl -s -X POST "https://api.pandorlabs.com/v1/scrapers/opensea-collections/runs" \ -H "Authorization: Bearer $PANDORLABS_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": { "sortBy": "VOLUME", "sortDirection": "DESC", "chains": [ "ethereum" ], "category": "PFPS" } }' | jq -r '.id')while true; do RUN=$(curl -s "https://api.pandorlabs.com/v1/runs/$RUN_ID" \ -H "Authorization: Bearer $PANDORLABS_KEY") STATUS=$(echo "$RUN" | jq -r '.status') BILLING=$(echo "$RUN" | jq -r '.billing.status') case "$STATUS" in queued|running) ;; *) [ "$BILLING" != "pending" ] && break ;; esac sleep 5doneecho "$RUN" | jq '{status, rowCount, billing}'curl -s "https://api.pandorlabs.com/v1/runs/$RUN_ID/rows?page=1&limit=100" \ -H "Authorization: Bearer $PANDORLABS_KEY"See the API reference for pagination, cancelling and listing runs.
Output fields
Rows have no fixed schema; these are the fields observed in the rows collected so far. Coverage is the share of rows where the field is present.
| Field | Type | Coverage |
|---|---|---|
| banner_image_url | string | 93% |
| category | string | 100% |
| chain | string | 100% |
| chain_arch | string | 100% |
| chain_name | string | 100% |
| chain_native_symbol | string | 100% |
| chain_network_id | number | 100% |
| collection_id | string | 100% |
| contract_address | string | 100% |
| creator_fee_basis_points | number | 100% |
| creator_fee_required | boolean | 100% |
| description | string | 94% |
| discord_url | string | 69% |
| external_url | string | 91% |
| featured_image_url | string | 82% |
| fifteen_minute | object | 100% |
| five_minute | object | 100% |
| floor_price_native | number | 99% |
| floor_price_symbol | string | 99% |
| floor_price_usd | number | 99% |
| floor_sparkline_7d | array | 99% |
| image_url | string | 100% |
| instagram_username | string | 42% |
| is_creator_fees_enforced | boolean | 100% |
| is_disabled | boolean | 100% |
| is_item_offers_enabled | boolean | 100% |
| is_offers_enabled | boolean | 100% |
| is_rarity_disabled | boolean | 100% |
| is_verified | boolean | 100% |
| listed_item_count | number | 100% |
| medium_username | string | 17% |
| metadata_storage | string | 100% |
| name | string | 100% |
| one_day | object | 100% |
| one_hour | object | 100% |
| one_minute | object | 100% |
| owner_address | string | 99% |
| owner_count | number | 100% |
| owner_display_name | string | 84% |
| owner_is_verified | boolean | 99% |
| owner_portfolio_usd | number | 99% |
| owner_twitter_username | string | 37% |
| owner_username | string | 82% |
| rank | number | 100% |
| sales_total | number | 100% |
| seven_days | object | 100% |
| slug | string | 100% |
| sort_by | string | 100% |
| sort_direction | string | 100% |
| telegram_url | string | 4% |
| thirty_days | object | 100% |
| token_standard | string | 100% |
| top_offer_native | number | 89% |
| top_offer_symbol | string | 89% |
| top_offer_usd | number | 89% |
| total_supply | number | 100% |
| twitter_username | string | 85% |
| unique_item_count | number | 100% |
| volume_native | number | 100% |
| volume_symbol | string | 100% |
| volume_usd | number | 100% |
| created_at | date | 100% |
| first_seen_at | date | 100% |
| last_seen_at | date | 100% |
| launched_at | date | 100% |
| updated_at | date | 100% |
| _id | string | 100% |
