Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ opensea search accounts <query> [--limit <n>]
## Tokens

```bash
opensea tokens trending [--chains <chains>] [--limit <n>] [--cursor <cursor>]
opensea tokens top [--chains <chains>] [--limit <n>] [--cursor <cursor>]
opensea tokens trending [--chains <chains>] [--limit <n>] [--next <cursor>]
opensea tokens top [--chains <chains>] [--limit <n>] [--next <cursor>]
opensea tokens get <chain> <address>
```

Expand All @@ -90,4 +90,4 @@ opensea swaps quote --from-chain <chain> --from-address <address> --to-chain <ch
opensea accounts get <address>
```

> All list commands support cursor-based pagination. See [pagination.md](pagination.md) for details.
> REST list commands support cursor-based pagination. Search commands return a flat list with no cursor. See [pagination.md](pagination.md) for details.
13 changes: 8 additions & 5 deletions docs/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The OpenSea API uses cursor-based pagination. Paginated responses include a `nex

## CLI

Most list commands support `--next <cursor>` (or `--cursor <cursor>` for tokens):
All paginated commands use `--next <cursor>`:

```bash
# First page
Expand All @@ -13,16 +13,17 @@ opensea collections list --limit 5
# The response includes a "next" cursor — pass it to get the next page
opensea collections list --limit 5 --next "LXBrPTEwMDA..."

# Tokens use --cursor instead of --next
# Tokens also use --next
opensea tokens trending --limit 5
opensea tokens trending --limit 5 --cursor "abc123..."
opensea tokens trending --limit 5 --next "abc123..."
```

### Commands that support pagination

| Command | Cursor flag |
|---|---|
| `collections list` | `--next` |

| `nfts list-by-collection` | `--next` |
| `nfts list-by-contract` | `--next` |
| `nfts list-by-account` | `--next` |
Expand All @@ -35,8 +36,10 @@ opensea tokens trending --limit 5 --cursor "abc123..."
| `events by-account` | `--next` |
| `events by-collection` | `--next` |
| `events by-nft` | `--next` |
| `tokens trending` | `--cursor` |
| `tokens top` | `--cursor` |
| `tokens trending` | `--next` |
| `tokens top` | `--next` |

> **Note:** Search commands (`search collections`, `search nfts`, `search tokens`, `search accounts`) do not support cursor-based pagination. The underlying GraphQL API returns a flat list with no `next` cursor.

## SDK

Expand Down
4 changes: 2 additions & 2 deletions docs/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const account = await client.accounts.get("0x123...")
const { tokens, next } = await client.tokens.trending({
chains: ["base", "ethereum"],
limit: 10,
cursor: "cursor_string",
next: "cursor_string",
})

const { tokens, next } = await client.tokens.top({
Expand All @@ -142,7 +142,7 @@ const tokenDetails = await client.tokens.get("base", "0x123...")

## Search

Search methods use GraphQL and return different result shapes than the REST API.
Search methods use GraphQL and return different result shapes than the REST API. Search endpoints do not currently expose a `next` cursor for pagination; use `limit` to control result count.

```typescript
const collections = await client.search.collections("mfers", {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opensea/cli",
"version": "0.2.1",
"version": "0.3.0",
"type": "module",
"description": "OpenSea CLI - Query the OpenSea API from the command line or programmatically",
"main": "dist/index.js",
Expand Down
12 changes: 6 additions & 6 deletions src/commands/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ export function tokensCommand(
.description("Get trending tokens based on OpenSea's trending score")
.option("--chains <chains>", "Comma-separated list of chains to filter by")
.option("--limit <limit>", "Number of results (max 100)", "20")
.option("--cursor <cursor>", "Pagination cursor")
.option("--next <cursor>", "Pagination cursor")
.action(
async (options: { chains?: string; limit: string; cursor?: string }) => {
async (options: { chains?: string; limit: string; next?: string }) => {
const client = getClient()
const result = await client.get<{ tokens: Token[]; next?: string }>(
"/api/v2/tokens/trending",
{
chains: options.chains,
limit: Number.parseInt(options.limit, 10),
cursor: options.cursor,
cursor: options.next,
},
)
console.log(formatOutput(result, getFormat()))
Expand All @@ -37,16 +37,16 @@ export function tokensCommand(
.description("Get top tokens ranked by 24-hour trading volume")
.option("--chains <chains>", "Comma-separated list of chains to filter by")
.option("--limit <limit>", "Number of results (max 100)", "20")
.option("--cursor <cursor>", "Pagination cursor")
.option("--next <cursor>", "Pagination cursor")
.action(
async (options: { chains?: string; limit: string; cursor?: string }) => {
async (options: { chains?: string; limit: string; next?: string }) => {
const client = getClient()
const result = await client.get<{ tokens: Token[]; next?: string }>(
"/api/v2/tokens/top",
{
chains: options.chains,
limit: Number.parseInt(options.limit, 10),
cursor: options.cursor,
cursor: options.next,
},
)
console.log(formatOutput(result, getFormat()))
Expand Down
8 changes: 4 additions & 4 deletions src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,24 +317,24 @@ class TokensAPI {
async trending(options?: {
limit?: number
chains?: string[]
cursor?: string
next?: string
}): Promise<{ tokens: Token[]; next?: string }> {
return this.client.get("/api/v2/tokens/trending", {
limit: options?.limit,
chains: options?.chains?.join(","),
cursor: options?.cursor,
cursor: options?.next,
})
}

async top(options?: {
limit?: number
chains?: string[]
cursor?: string
next?: string
}): Promise<{ tokens: Token[]; next?: string }> {
return this.client.get("/api/v2/tokens/top", {
limit: options?.limit,
chains: options?.chains?.join(","),
cursor: options?.cursor,
cursor: options?.next,
})
}

Expand Down
5 changes: 0 additions & 5 deletions src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,6 @@ export interface GetTraitsResponse {
counts: { [traitType: string]: TraitCounts }
}

export interface PaginatedResponse<T> {
next?: string
results: T[]
}

export interface Token {
address: string
chain: string
Expand Down
17 changes: 14 additions & 3 deletions test/commands/tokens.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@ describe("tokensCommand", () => {

const cmd = tokensCommand(ctx.getClient, ctx.getFormat)
await cmd.parseAsync(
["trending", "--chains", "ethereum,base", "--limit", "10"],
[
"trending",
"--chains",
"ethereum,base",
"--limit",
"10",
"--next",
"abc123",
],
{ from: "user" },
)

Expand All @@ -36,6 +44,7 @@ describe("tokensCommand", () => {
expect.objectContaining({
chains: "ethereum,base",
limit: 10,
cursor: "abc123",
}),
)
})
Expand All @@ -44,11 +53,13 @@ describe("tokensCommand", () => {
ctx.mockClient.get.mockResolvedValue({ tokens: [] })

const cmd = tokensCommand(ctx.getClient, ctx.getFormat)
await cmd.parseAsync(["top", "--limit", "5"], { from: "user" })
await cmd.parseAsync(["top", "--limit", "5", "--next", "cursor1"], {
from: "user",
})

expect(ctx.mockClient.get).toHaveBeenCalledWith(
"/api/v2/tokens/top",
expect.objectContaining({ limit: 5 }),
expect.objectContaining({ limit: 5, cursor: "cursor1" }),
)
})

Expand Down
20 changes: 20 additions & 0 deletions test/sdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,16 @@ describe("OpenSeaCLI", () => {
})
})

it("trending maps next option to cursor param", async () => {
mockGet.mockResolvedValue({ tokens: [] })
await sdk.tokens.trending({ next: "cursor1" })
expect(mockGet).toHaveBeenCalledWith("/api/v2/tokens/trending", {
limit: undefined,
chains: undefined,
cursor: "cursor1",
})
})

it("trending with no options", async () => {
mockGet.mockResolvedValue({ tokens: [] })
await sdk.tokens.trending()
Expand All @@ -304,6 +314,16 @@ describe("OpenSeaCLI", () => {
})
})

it("top maps next option to cursor param", async () => {
mockGet.mockResolvedValue({ tokens: [] })
await sdk.tokens.top({ limit: 5, next: "cursor2" })
expect(mockGet).toHaveBeenCalledWith("/api/v2/tokens/top", {
limit: 5,
chains: undefined,
cursor: "cursor2",
})
})

it("top calls correct endpoint", async () => {
mockGet.mockResolvedValue({ tokens: [] })
await sdk.tokens.top({ limit: 5 })
Expand Down