API, MCP & CLI

API: Getting started

Create a ListingView API key or a test key, make your first call, rehearse safely, and learn the rules every call shares: idempotency keys, pagination, errors, retries and key expiry.

Updated

Connect Claude, ChatGPT or Cursor to ListingView to research what's selling on Etsy, update your Etsy shop, manage your listings.

The ListingView API lets your scripts, the ListingView CLI and AI apps over MCP work with your ListingView account. This guide takes you from a new key to your first call, then covers the rules every call shares.

New to API keys? Using the ListingView API, MCP server & CLI explains keys, access presets and the Etsy drafts workflow in plain words.

What you can do

  • Research the Etsy market: keywords, tags, and other shops and listings.

  • Read your ListingView listings, jobs, presets and asset library, and your own shop's stats.

  • Upload designs, upload the files your digital listings deliver, and import images into your asset library. Then organize them: collections, names, tags and copies. See API: Uploads.

  • Build presets: print-on-demand, digital and physical, from the products, print providers, category attributes, shop profiles and mockup templates they're made of. See Build presets with the API.

  • Create and edit listings in ListingView. These changes stay in ListingView and never reach Etsy.

  • Get AI-written titles, tags and descriptions from your artwork, or improve copy you already have. This spends AI tokens.

  • Turn a preset and your designs into Etsy drafts (Plus plan and above). Stage a batch first so you can review it in ListingView, then submit it. Shoppers can't see drafts. See Stage Etsy drafts, review them in ListingView, then submit.

  • Publish listings and reprice live print-on-demand listings, each through a confirm step (Plus plan and above). See Publish and reprice live listings.

1. Create a key

API keys need a paid ListingView plan.

  1. In ListingView, open Settings → API Keys and click Create key.

  2. Give the key a name and choose what it may do. Pick an access preset, or choose scopes one by one (see the scopes list).

    • A Research Assistant key can only read.

    • A Catalog Editor key works inside ListingView only.

    • A Drafting Agent key can also create Etsy drafts. It needs the Plus plan or higher.

    • A Publisher key publishes and reprices on Etsy, through a confirm step. It needs the Plus plan or higher.

  3. To rehearse without changing anything, tick Test key (no real changes) under Access. The button then reads Create test key.

  4. Copy the key and keep it somewhere safe. It is shown only once.

Live keys start with lv_live_, and test keys start with lv_test_.

Treat a key like a password. Don't commit it to a repository or paste it into a web page. The API sends no CORS headers, so call it from a server, a script, the CLI or an AI app, never from browser code.

Drafting and publishing use two keys

A key that can create Etsy drafts can never publish, and a key that can publish can never create drafts. Settings won't combine the two.

  • So an agent that drafts and also publishes needs two keys: a Drafting Agent key and a Publisher key.

  • The Drafting Agent key builds presets, uploads files, writes AI copy, and stages and submits Etsy drafts.

  • The Publisher key publishes listings and reprices live ones.

  • Keep each key in its own app, chat or script. Then text hidden in a listing can't steer your drafting agent into publishing.

With the CLI, save each key under its own profile, for example --profile drafts and --profile publisher. API: Connect AI apps shows the one-server-per-app setup for AI apps.

2. Send the key

Send the key as a Bearer token in the Authorization header of every request. The base URL is https://api.listingview.io/api/v1.

Authorization: Bearer lv_live_…

A missing, mistyped, revoked or expired key gets 401 UNAUTHORIZED. On purpose, the error doesn't say which part failed. There are two exceptions: a key that stopped because it wasn't used gets 401 KEY_EXPIRED (see Keys that stop working below), and a key whose scopes can no longer be held together gets 403 SCOPE_CONFLICT. If ListingView itself can't check keys for a moment, you get a retryable 503 SERVICE_UNAVAILABLE, never a 401.

3. Make your first call

GET /ping works with any valid key and is free. It tells you whether the key is a test key and which scopes it holds.

curl https://api.listingview.io/api/v1/ping \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
{
  "ok": true,
  "transport": "rest",
  "request_id": "01JABCDEF0123456789XYZABCD",
  "server_time": "2026-10-15T12:00:00.000Z",
  "test_key": false,
  "scopes": ["research:read"]
}

Next, check your budgets. GET /usage is free too. It needs the research:read scope, which every access preset except Publisher includes. See API: Limits & budgets.

curl https://api.listingview.io/api/v1/usage \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

Send request bodies as JSON with Content-Type: application/json. Every response carries an X-LV-Request-Id header. Quote it when you contact support.

A request string that contains a NUL character (U+0000), anywhere in the body, the query or the path, is refused before anything runs: 400 VALIDATION_FAILED, with details naming the field. Strip NUL characters from text before you send it.

Every route, with its parameters, scopes and credit cost, is in the API reference. The reference is generated live from the API, so it always matches what the server does.

Rehearse with a test key

A test key lets you run a whole flow against your real account without changing anything. It has the same scopes and plan rules as a live key with the same access, and it counts toward your key limit. GET /ping answers "test_key": true.

With a test key

What happens

Writes (presets, uploads, listings, staged batches, Etsy drafts, deletes, publishing)

Checked like a live call, then answered as a dry run. Free.

AI copy

Answered as a dry run. No AI tokens are used.

Reads of your ListingView data and market research

Real reads of your real data, charged the same as with a live key.

Checks that would need Etsy or Printify

Answered from what ListingView has cached, or reported as unverified.

Dry runs. A dry run answers with status 200, the header X-LV-Dry-Run: true and a body with "dry_run": true. The body carries a plan of what would have happened, for example would_stage. Nothing is written, nothing reaches Etsy or Printify, no notification is sent and nothing stays charged. A budget that is already used up still refuses the call, as it would a live one.

unverified. A test key never calls Etsy or Printify. A check that needs them, such as a shop's shipping profiles or a listing's live state before a delete, uses ListingView's cached data. When there is none, the check comes back unverified. A dry run with an unverified check doesn't prove the live call will pass, so run that step once with a live key before you rely on it.

Some special cases:

  • Publishing and reprices. The confirm step runs for real. Confirming a reprice then answers with would.would_push and the price changes it would make, with the live prices unverified. Confirming a push or publish answers with would.live_write, describing the write it would make.

  • Market research. Rows that would need a refresh from Etsy come back as pointers with pointer_reason: "test_key". The rest of the answer is real and charged as usual. See API: Limits & budgets.

  • Idempotency keys. Test keys and live keys keep separate records, so a rehearsal never replays into a live call.

  • Rate limits. Test keys have their own burst and concurrency limits, so a rehearsal doesn't slow your live keys.

When the rehearsal looks right, swap in a live key with the same access and run the flow again.

Retry safely with idempotency keys

Scripts and agents retry. To make a retried change safe, send an X-Idempotency-Key header on any call that changes data. Use up to 128 letters, digits, _ and -, and a new key for each new change. MCP tools take the same key as an idempotency_key argument.

curl -X POST https://api.listingview.io/api/v1/assets/collections \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY" \
  -H "X-Idempotency-Key: halloween-2026-collection" \
  -H "Content-Type: application/json" \
  -d '{"name": "Halloween 2026"}'
  • Same key and same body within 24 hours: you get the first answer back, with the header X-LV-Idempotent-Replay: true. Nothing runs twice, and the replay costs nothing.

  • Same key with a different body: 409 CONFLICT with details.reason: "idempotency_key_reused".

  • Same key while the first call is still running: the API waits up to 5 seconds, then answers 409 CONFLICT with details.reason: "in_flight". Retry after retry_after.

  • Failed calls aren't stored. Retrying after an error runs the change again.

  • Very large answers. An answer over 1 MB can't be kept for a replay. Sending its key again answers replay_unavailable.

These routes require an idempotency key. Several keep the key with the thing they made, so their replay marks itself with "replayed": true in the body rather than the header:

Route

How long the same key returns the same answer

POST /api/v1/assets/uploads

24 hours. A call with the same key that is still running answers in_flight at once.

POST /api/v1/assets/files/{file_id}/duplicate

Always: the same key always means the same copy.

POST /api/v1/etsy-drafts (drafts now)

As long as the job exists.

POST /api/v1/etsy-drafts with "submit": false (stage)

While the batch is still open.

PATCH /api/v1/etsy-drafts/staged/{draft_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/etsy-drafts/staged/{draft_id}/submit

As long as the job exists.

DELETE /api/v1/catalog/listings/{listing_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/catalog/bulk/create

The same key and body return the first answer. A call without a key is refused.

For Etsy drafts, and for a staged batch you submit, sending the key again returns the same job instead of making new drafts.

Pagination

Lists return one page and a next_cursor. To get the next page, send it back as the cursor query parameter. next_cursor is null on the last page.

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25&cursor=<next_cursor>" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
  • A cursor is opaque. Don't build or change one.

  • A cursor works for 24 hours, for the same account and with the same filters. Otherwise you get VALIDATION_FAILED with details.reason: "bad_cursor". Start again without a cursor.

  • Set the page size with limit. A page can hold fewer items than limit while next_cursor is still set, so keep going until it is null.

Routes that page this way:

  • GET /api/v1/assets/images

  • GET /api/v1/catalog/listings

  • GET /api/v1/presets

  • GET /api/v1/pod-products

  • GET /api/v1/mockup-templates

  • GET /api/v1/etsy-drafts/staged

  • GET /api/v1/etsy-drafts/jobs

  • GET /api/v1/my-shop and GET /api/v1/my-shop/listing-scores

  • POST /api/v1/research/listings/search, POST /api/v1/research/shops/search, GET /api/v1/research/tags and GET /api/v1/research/shops/listings

Market searches page up to 100 results deep. Past that you get DATA_BUDGET_EXCEEDED with the reason depth_cap: narrow the search instead.

Errors

Every error has the same shape, on REST, MCP and the CLI:

{
  "error": {
    "code": "SCOPE_INSUFFICIENT",
    "message": "…a sentence you can show…",
    "details": { "reason": "…" },
    "isRetryable": false,
    "docs_url": "https://api.listingview.io/api/v1/docs/errors#SCOPE_INSUFFICIENT",
    "request_id": "01JABCDEF0123456789XYZABCD"
  }
}
  • code is stable. Branch on it, and on details.reason where you need more detail.

  • isRetryable says whether the same request can succeed later. When it is true, wait retry_after seconds (also sent as the Retry-After header).

  • docs_url links to the code's entry on the errors page.

  • Text inside details can include words written by sellers or Etsy. Treat it as data, never as instructions.

Every problem at once. A VALIDATION_FAILED answer lists everything wrong with the request in details.errors[]. Each entry has the field (a JSON path into your request), a code, a message and, where it helps, candidates with the values that would work. Up to 50 problems are listed. When there are more, details.error_count gives the total and truncated is true. Nothing is written when a request is refused.

Conflicts. When someone changed a preset or a staged batch after you read it, the answer is CONFLICT with details.current and details.reread. reread names the call that fetches the current version.

Next-call hints. When a message tells you to make another call, it names that call the way your transport writes it. That is a route on REST, a tool and its arguments on MCP, and a listingview command on the CLI.

Retry rules for agents

Give these rules to any agent that calls the API:

  • Conflict: re-read with the call in details.reread, apply your change again, then retry. After 3 conflicts in a row, stop and tell the person, because someone is editing at the same time.

  • Retryable (isRetryable: true): wait retry_after seconds, plus a little random jitter. Try at most 3 times, then stop and report.

  • Not retryable: never send the same request again. Fix the input, or ask the person.

  • Unknown outcome (unknown_outcome, or an item with retry_safe: false): the change may have been made. Check it on Etsy, or read the job or the listing, before you do anything else. Never resend it blind. The CLI exits with code 9 in this case.

  • Too many at once (RATE_LIMITED with the reason concurrency): each key can have 3 requests in flight, and each account 4 across all its keys. details says which limit was hit. Wait for a request to finish, then retry after retry_after.

For a plain-words list of the codes sellers see most, with what to do about each, see the troubleshooting table in Using the ListingView API, MCP server & CLI.

Keys that stop working

  • Revoked. Revoke a key in Settings → API Keys. It stops working within a minute.

    • Revoking a key also cancels its unfinished jobs: Etsy-draft jobs for a Drafting Agent key, and reprice jobs for a Publisher key. Drafts and changes it already made stay. Any confirmation it was waiting on stops working.

    • Batches it staged stay in Saved listings. Another key on your account with the same access can submit or discard them.

    • If ListingView support revokes a key, for example after a leak, that key's staged batches are discarded too.

  • Expired. If you gave the key an expiry date when you created it, it stops working on that date.

  • Unused. A key that can change your Etsy shop (a Drafting Agent or Publisher key) stops working after 60 days without a request (the default). Settings shows the date. Calls then get 401 KEY_EXPIRED with details.reason: "idle"; create a new key. Keys that can't change your Etsy shop don't stop this way.

  • Plan changes. Your plan is checked on every request, so a plan change applies at once.

Signing out of the CLI (listingview auth logout) removes the key from that computer only. It doesn't revoke the key.

Related features

The ListingView API lets your scripts, the ListingView CLI and AI apps over MCP work with your ListingView account. This guide takes you from a new key to your first call, then covers the rules every call shares.

New to API keys? Using the ListingView API, MCP server & CLI explains keys, access presets and the Etsy drafts workflow in plain words.

What you can do

  • Research the Etsy market: keywords, tags, and other shops and listings.

  • Read your ListingView listings, jobs, presets and asset library, and your own shop's stats.

  • Upload designs, upload the files your digital listings deliver, and import images into your asset library. Then organize them: collections, names, tags and copies. See API: Uploads.

  • Build presets: print-on-demand, digital and physical, from the products, print providers, category attributes, shop profiles and mockup templates they're made of. See Build presets with the API.

  • Create and edit listings in ListingView. These changes stay in ListingView and never reach Etsy.

  • Get AI-written titles, tags and descriptions from your artwork, or improve copy you already have. This spends AI tokens.

  • Turn a preset and your designs into Etsy drafts (Plus plan and above). Stage a batch first so you can review it in ListingView, then submit it. Shoppers can't see drafts. See Stage Etsy drafts, review them in ListingView, then submit.

  • Publish listings and reprice live print-on-demand listings, each through a confirm step (Plus plan and above). See Publish and reprice live listings.

1. Create a key

API keys need a paid ListingView plan.

  1. In ListingView, open Settings → API Keys and click Create key.

  2. Give the key a name and choose what it may do. Pick an access preset, or choose scopes one by one (see the scopes list).

    • A Research Assistant key can only read.

    • A Catalog Editor key works inside ListingView only.

    • A Drafting Agent key can also create Etsy drafts. It needs the Plus plan or higher.

    • A Publisher key publishes and reprices on Etsy, through a confirm step. It needs the Plus plan or higher.

  3. To rehearse without changing anything, tick Test key (no real changes) under Access. The button then reads Create test key.

  4. Copy the key and keep it somewhere safe. It is shown only once.

Live keys start with lv_live_, and test keys start with lv_test_.

Treat a key like a password. Don't commit it to a repository or paste it into a web page. The API sends no CORS headers, so call it from a server, a script, the CLI or an AI app, never from browser code.

Drafting and publishing use two keys

A key that can create Etsy drafts can never publish, and a key that can publish can never create drafts. Settings won't combine the two.

  • So an agent that drafts and also publishes needs two keys: a Drafting Agent key and a Publisher key.

  • The Drafting Agent key builds presets, uploads files, writes AI copy, and stages and submits Etsy drafts.

  • The Publisher key publishes listings and reprices live ones.

  • Keep each key in its own app, chat or script. Then text hidden in a listing can't steer your drafting agent into publishing.

With the CLI, save each key under its own profile, for example --profile drafts and --profile publisher. API: Connect AI apps shows the one-server-per-app setup for AI apps.

2. Send the key

Send the key as a Bearer token in the Authorization header of every request. The base URL is https://api.listingview.io/api/v1.

Authorization: Bearer lv_live_…

A missing, mistyped, revoked or expired key gets 401 UNAUTHORIZED. On purpose, the error doesn't say which part failed. There are two exceptions: a key that stopped because it wasn't used gets 401 KEY_EXPIRED (see Keys that stop working below), and a key whose scopes can no longer be held together gets 403 SCOPE_CONFLICT. If ListingView itself can't check keys for a moment, you get a retryable 503 SERVICE_UNAVAILABLE, never a 401.

3. Make your first call

GET /ping works with any valid key and is free. It tells you whether the key is a test key and which scopes it holds.

curl https://api.listingview.io/api/v1/ping \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
{
  "ok": true,
  "transport": "rest",
  "request_id": "01JABCDEF0123456789XYZABCD",
  "server_time": "2026-10-15T12:00:00.000Z",
  "test_key": false,
  "scopes": ["research:read"]
}

Next, check your budgets. GET /usage is free too. It needs the research:read scope, which every access preset except Publisher includes. See API: Limits & budgets.

curl https://api.listingview.io/api/v1/usage \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

Send request bodies as JSON with Content-Type: application/json. Every response carries an X-LV-Request-Id header. Quote it when you contact support.

A request string that contains a NUL character (U+0000), anywhere in the body, the query or the path, is refused before anything runs: 400 VALIDATION_FAILED, with details naming the field. Strip NUL characters from text before you send it.

Every route, with its parameters, scopes and credit cost, is in the API reference. The reference is generated live from the API, so it always matches what the server does.

Rehearse with a test key

A test key lets you run a whole flow against your real account without changing anything. It has the same scopes and plan rules as a live key with the same access, and it counts toward your key limit. GET /ping answers "test_key": true.

With a test key

What happens

Writes (presets, uploads, listings, staged batches, Etsy drafts, deletes, publishing)

Checked like a live call, then answered as a dry run. Free.

AI copy

Answered as a dry run. No AI tokens are used.

Reads of your ListingView data and market research

Real reads of your real data, charged the same as with a live key.

Checks that would need Etsy or Printify

Answered from what ListingView has cached, or reported as unverified.

Dry runs. A dry run answers with status 200, the header X-LV-Dry-Run: true and a body with "dry_run": true. The body carries a plan of what would have happened, for example would_stage. Nothing is written, nothing reaches Etsy or Printify, no notification is sent and nothing stays charged. A budget that is already used up still refuses the call, as it would a live one.

unverified. A test key never calls Etsy or Printify. A check that needs them, such as a shop's shipping profiles or a listing's live state before a delete, uses ListingView's cached data. When there is none, the check comes back unverified. A dry run with an unverified check doesn't prove the live call will pass, so run that step once with a live key before you rely on it.

Some special cases:

  • Publishing and reprices. The confirm step runs for real. Confirming a reprice then answers with would.would_push and the price changes it would make, with the live prices unverified. Confirming a push or publish answers with would.live_write, describing the write it would make.

  • Market research. Rows that would need a refresh from Etsy come back as pointers with pointer_reason: "test_key". The rest of the answer is real and charged as usual. See API: Limits & budgets.

  • Idempotency keys. Test keys and live keys keep separate records, so a rehearsal never replays into a live call.

  • Rate limits. Test keys have their own burst and concurrency limits, so a rehearsal doesn't slow your live keys.

When the rehearsal looks right, swap in a live key with the same access and run the flow again.

Retry safely with idempotency keys

Scripts and agents retry. To make a retried change safe, send an X-Idempotency-Key header on any call that changes data. Use up to 128 letters, digits, _ and -, and a new key for each new change. MCP tools take the same key as an idempotency_key argument.

curl -X POST https://api.listingview.io/api/v1/assets/collections \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY" \
  -H "X-Idempotency-Key: halloween-2026-collection" \
  -H "Content-Type: application/json" \
  -d '{"name": "Halloween 2026"}'
  • Same key and same body within 24 hours: you get the first answer back, with the header X-LV-Idempotent-Replay: true. Nothing runs twice, and the replay costs nothing.

  • Same key with a different body: 409 CONFLICT with details.reason: "idempotency_key_reused".

  • Same key while the first call is still running: the API waits up to 5 seconds, then answers 409 CONFLICT with details.reason: "in_flight". Retry after retry_after.

  • Failed calls aren't stored. Retrying after an error runs the change again.

  • Very large answers. An answer over 1 MB can't be kept for a replay. Sending its key again answers replay_unavailable.

These routes require an idempotency key. Several keep the key with the thing they made, so their replay marks itself with "replayed": true in the body rather than the header:

Route

How long the same key returns the same answer

POST /api/v1/assets/uploads

24 hours. A call with the same key that is still running answers in_flight at once.

POST /api/v1/assets/files/{file_id}/duplicate

Always: the same key always means the same copy.

POST /api/v1/etsy-drafts (drafts now)

As long as the job exists.

POST /api/v1/etsy-drafts with "submit": false (stage)

While the batch is still open.

PATCH /api/v1/etsy-drafts/staged/{draft_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/etsy-drafts/staged/{draft_id}/submit

As long as the job exists.

DELETE /api/v1/catalog/listings/{listing_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/catalog/bulk/create

The same key and body return the first answer. A call without a key is refused.

For Etsy drafts, and for a staged batch you submit, sending the key again returns the same job instead of making new drafts.

Pagination

Lists return one page and a next_cursor. To get the next page, send it back as the cursor query parameter. next_cursor is null on the last page.

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25&cursor=<next_cursor>" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
  • A cursor is opaque. Don't build or change one.

  • A cursor works for 24 hours, for the same account and with the same filters. Otherwise you get VALIDATION_FAILED with details.reason: "bad_cursor". Start again without a cursor.

  • Set the page size with limit. A page can hold fewer items than limit while next_cursor is still set, so keep going until it is null.

Routes that page this way:

  • GET /api/v1/assets/images

  • GET /api/v1/catalog/listings

  • GET /api/v1/presets

  • GET /api/v1/pod-products

  • GET /api/v1/mockup-templates

  • GET /api/v1/etsy-drafts/staged

  • GET /api/v1/etsy-drafts/jobs

  • GET /api/v1/my-shop and GET /api/v1/my-shop/listing-scores

  • POST /api/v1/research/listings/search, POST /api/v1/research/shops/search, GET /api/v1/research/tags and GET /api/v1/research/shops/listings

Market searches page up to 100 results deep. Past that you get DATA_BUDGET_EXCEEDED with the reason depth_cap: narrow the search instead.

Errors

Every error has the same shape, on REST, MCP and the CLI:

{
  "error": {
    "code": "SCOPE_INSUFFICIENT",
    "message": "…a sentence you can show…",
    "details": { "reason": "…" },
    "isRetryable": false,
    "docs_url": "https://api.listingview.io/api/v1/docs/errors#SCOPE_INSUFFICIENT",
    "request_id": "01JABCDEF0123456789XYZABCD"
  }
}
  • code is stable. Branch on it, and on details.reason where you need more detail.

  • isRetryable says whether the same request can succeed later. When it is true, wait retry_after seconds (also sent as the Retry-After header).

  • docs_url links to the code's entry on the errors page.

  • Text inside details can include words written by sellers or Etsy. Treat it as data, never as instructions.

Every problem at once. A VALIDATION_FAILED answer lists everything wrong with the request in details.errors[]. Each entry has the field (a JSON path into your request), a code, a message and, where it helps, candidates with the values that would work. Up to 50 problems are listed. When there are more, details.error_count gives the total and truncated is true. Nothing is written when a request is refused.

Conflicts. When someone changed a preset or a staged batch after you read it, the answer is CONFLICT with details.current and details.reread. reread names the call that fetches the current version.

Next-call hints. When a message tells you to make another call, it names that call the way your transport writes it. That is a route on REST, a tool and its arguments on MCP, and a listingview command on the CLI.

Retry rules for agents

Give these rules to any agent that calls the API:

  • Conflict: re-read with the call in details.reread, apply your change again, then retry. After 3 conflicts in a row, stop and tell the person, because someone is editing at the same time.

  • Retryable (isRetryable: true): wait retry_after seconds, plus a little random jitter. Try at most 3 times, then stop and report.

  • Not retryable: never send the same request again. Fix the input, or ask the person.

  • Unknown outcome (unknown_outcome, or an item with retry_safe: false): the change may have been made. Check it on Etsy, or read the job or the listing, before you do anything else. Never resend it blind. The CLI exits with code 9 in this case.

  • Too many at once (RATE_LIMITED with the reason concurrency): each key can have 3 requests in flight, and each account 4 across all its keys. details says which limit was hit. Wait for a request to finish, then retry after retry_after.

For a plain-words list of the codes sellers see most, with what to do about each, see the troubleshooting table in Using the ListingView API, MCP server & CLI.

Keys that stop working

  • Revoked. Revoke a key in Settings → API Keys. It stops working within a minute.

    • Revoking a key also cancels its unfinished jobs: Etsy-draft jobs for a Drafting Agent key, and reprice jobs for a Publisher key. Drafts and changes it already made stay. Any confirmation it was waiting on stops working.

    • Batches it staged stay in Saved listings. Another key on your account with the same access can submit or discard them.

    • If ListingView support revokes a key, for example after a leak, that key's staged batches are discarded too.

  • Expired. If you gave the key an expiry date when you created it, it stops working on that date.

  • Unused. A key that can change your Etsy shop (a Drafting Agent or Publisher key) stops working after 60 days without a request (the default). Settings shows the date. Calls then get 401 KEY_EXPIRED with details.reason: "idle"; create a new key. Keys that can't change your Etsy shop don't stop this way.

  • Plan changes. Your plan is checked on every request, so a plan change applies at once.

Signing out of the CLI (listingview auth logout) removes the key from that computer only. It doesn't revoke the key.

Related features

The ListingView API lets your scripts, the ListingView CLI and AI apps over MCP work with your ListingView account. This guide takes you from a new key to your first call, then covers the rules every call shares.

New to API keys? Using the ListingView API, MCP server & CLI explains keys, access presets and the Etsy drafts workflow in plain words.

What you can do

  • Research the Etsy market: keywords, tags, and other shops and listings.

  • Read your ListingView listings, jobs, presets and asset library, and your own shop's stats.

  • Upload designs, upload the files your digital listings deliver, and import images into your asset library. Then organize them: collections, names, tags and copies. See API: Uploads.

  • Build presets: print-on-demand, digital and physical, from the products, print providers, category attributes, shop profiles and mockup templates they're made of. See Build presets with the API.

  • Create and edit listings in ListingView. These changes stay in ListingView and never reach Etsy.

  • Get AI-written titles, tags and descriptions from your artwork, or improve copy you already have. This spends AI tokens.

  • Turn a preset and your designs into Etsy drafts (Plus plan and above). Stage a batch first so you can review it in ListingView, then submit it. Shoppers can't see drafts. See Stage Etsy drafts, review them in ListingView, then submit.

  • Publish listings and reprice live print-on-demand listings, each through a confirm step (Plus plan and above). See Publish and reprice live listings.

1. Create a key

API keys need a paid ListingView plan.

  1. In ListingView, open Settings → API Keys and click Create key.

  2. Give the key a name and choose what it may do. Pick an access preset, or choose scopes one by one (see the scopes list).

    • A Research Assistant key can only read.

    • A Catalog Editor key works inside ListingView only.

    • A Drafting Agent key can also create Etsy drafts. It needs the Plus plan or higher.

    • A Publisher key publishes and reprices on Etsy, through a confirm step. It needs the Plus plan or higher.

  3. To rehearse without changing anything, tick Test key (no real changes) under Access. The button then reads Create test key.

  4. Copy the key and keep it somewhere safe. It is shown only once.

Live keys start with lv_live_, and test keys start with lv_test_.

Treat a key like a password. Don't commit it to a repository or paste it into a web page. The API sends no CORS headers, so call it from a server, a script, the CLI or an AI app, never from browser code.

Drafting and publishing use two keys

A key that can create Etsy drafts can never publish, and a key that can publish can never create drafts. Settings won't combine the two.

  • So an agent that drafts and also publishes needs two keys: a Drafting Agent key and a Publisher key.

  • The Drafting Agent key builds presets, uploads files, writes AI copy, and stages and submits Etsy drafts.

  • The Publisher key publishes listings and reprices live ones.

  • Keep each key in its own app, chat or script. Then text hidden in a listing can't steer your drafting agent into publishing.

With the CLI, save each key under its own profile, for example --profile drafts and --profile publisher. API: Connect AI apps shows the one-server-per-app setup for AI apps.

2. Send the key

Send the key as a Bearer token in the Authorization header of every request. The base URL is https://api.listingview.io/api/v1.

Authorization: Bearer lv_live_…

A missing, mistyped, revoked or expired key gets 401 UNAUTHORIZED. On purpose, the error doesn't say which part failed. There are two exceptions: a key that stopped because it wasn't used gets 401 KEY_EXPIRED (see Keys that stop working below), and a key whose scopes can no longer be held together gets 403 SCOPE_CONFLICT. If ListingView itself can't check keys for a moment, you get a retryable 503 SERVICE_UNAVAILABLE, never a 401.

3. Make your first call

GET /ping works with any valid key and is free. It tells you whether the key is a test key and which scopes it holds.

curl https://api.listingview.io/api/v1/ping \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
{
  "ok": true,
  "transport": "rest",
  "request_id": "01JABCDEF0123456789XYZABCD",
  "server_time": "2026-10-15T12:00:00.000Z",
  "test_key": false,
  "scopes": ["research:read"]
}

Next, check your budgets. GET /usage is free too. It needs the research:read scope, which every access preset except Publisher includes. See API: Limits & budgets.

curl https://api.listingview.io/api/v1/usage \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

Send request bodies as JSON with Content-Type: application/json. Every response carries an X-LV-Request-Id header. Quote it when you contact support.

A request string that contains a NUL character (U+0000), anywhere in the body, the query or the path, is refused before anything runs: 400 VALIDATION_FAILED, with details naming the field. Strip NUL characters from text before you send it.

Every route, with its parameters, scopes and credit cost, is in the API reference. The reference is generated live from the API, so it always matches what the server does.

Rehearse with a test key

A test key lets you run a whole flow against your real account without changing anything. It has the same scopes and plan rules as a live key with the same access, and it counts toward your key limit. GET /ping answers "test_key": true.

With a test key

What happens

Writes (presets, uploads, listings, staged batches, Etsy drafts, deletes, publishing)

Checked like a live call, then answered as a dry run. Free.

AI copy

Answered as a dry run. No AI tokens are used.

Reads of your ListingView data and market research

Real reads of your real data, charged the same as with a live key.

Checks that would need Etsy or Printify

Answered from what ListingView has cached, or reported as unverified.

Dry runs. A dry run answers with status 200, the header X-LV-Dry-Run: true and a body with "dry_run": true. The body carries a plan of what would have happened, for example would_stage. Nothing is written, nothing reaches Etsy or Printify, no notification is sent and nothing stays charged. A budget that is already used up still refuses the call, as it would a live one.

unverified. A test key never calls Etsy or Printify. A check that needs them, such as a shop's shipping profiles or a listing's live state before a delete, uses ListingView's cached data. When there is none, the check comes back unverified. A dry run with an unverified check doesn't prove the live call will pass, so run that step once with a live key before you rely on it.

Some special cases:

  • Publishing and reprices. The confirm step runs for real. Confirming a reprice then answers with would.would_push and the price changes it would make, with the live prices unverified. Confirming a push or publish answers with would.live_write, describing the write it would make.

  • Market research. Rows that would need a refresh from Etsy come back as pointers with pointer_reason: "test_key". The rest of the answer is real and charged as usual. See API: Limits & budgets.

  • Idempotency keys. Test keys and live keys keep separate records, so a rehearsal never replays into a live call.

  • Rate limits. Test keys have their own burst and concurrency limits, so a rehearsal doesn't slow your live keys.

When the rehearsal looks right, swap in a live key with the same access and run the flow again.

Retry safely with idempotency keys

Scripts and agents retry. To make a retried change safe, send an X-Idempotency-Key header on any call that changes data. Use up to 128 letters, digits, _ and -, and a new key for each new change. MCP tools take the same key as an idempotency_key argument.

curl -X POST https://api.listingview.io/api/v1/assets/collections \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY" \
  -H "X-Idempotency-Key: halloween-2026-collection" \
  -H "Content-Type: application/json" \
  -d '{"name": "Halloween 2026"}'
  • Same key and same body within 24 hours: you get the first answer back, with the header X-LV-Idempotent-Replay: true. Nothing runs twice, and the replay costs nothing.

  • Same key with a different body: 409 CONFLICT with details.reason: "idempotency_key_reused".

  • Same key while the first call is still running: the API waits up to 5 seconds, then answers 409 CONFLICT with details.reason: "in_flight". Retry after retry_after.

  • Failed calls aren't stored. Retrying after an error runs the change again.

  • Very large answers. An answer over 1 MB can't be kept for a replay. Sending its key again answers replay_unavailable.

These routes require an idempotency key. Several keep the key with the thing they made, so their replay marks itself with "replayed": true in the body rather than the header:

Route

How long the same key returns the same answer

POST /api/v1/assets/uploads

24 hours. A call with the same key that is still running answers in_flight at once.

POST /api/v1/assets/files/{file_id}/duplicate

Always: the same key always means the same copy.

POST /api/v1/etsy-drafts (drafts now)

As long as the job exists.

POST /api/v1/etsy-drafts with "submit": false (stage)

While the batch is still open.

PATCH /api/v1/etsy-drafts/staged/{draft_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/etsy-drafts/staged/{draft_id}/submit

As long as the job exists.

DELETE /api/v1/catalog/listings/{listing_id}

24 hours, with the X-LV-Idempotent-Replay header.

POST /api/v1/catalog/bulk/create

The same key and body return the first answer. A call without a key is refused.

For Etsy drafts, and for a staged batch you submit, sending the key again returns the same job instead of making new drafts.

Pagination

Lists return one page and a next_cursor. To get the next page, send it back as the cursor query parameter. next_cursor is null on the last page.

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"

curl "https://api.listingview.io/api/v1/catalog/listings?status=draft&limit=25&cursor=<next_cursor>" \
  -H "Authorization: Bearer $LISTINGVIEW_API_KEY"
  • A cursor is opaque. Don't build or change one.

  • A cursor works for 24 hours, for the same account and with the same filters. Otherwise you get VALIDATION_FAILED with details.reason: "bad_cursor". Start again without a cursor.

  • Set the page size with limit. A page can hold fewer items than limit while next_cursor is still set, so keep going until it is null.

Routes that page this way:

  • GET /api/v1/assets/images

  • GET /api/v1/catalog/listings

  • GET /api/v1/presets

  • GET /api/v1/pod-products

  • GET /api/v1/mockup-templates

  • GET /api/v1/etsy-drafts/staged

  • GET /api/v1/etsy-drafts/jobs

  • GET /api/v1/my-shop and GET /api/v1/my-shop/listing-scores

  • POST /api/v1/research/listings/search, POST /api/v1/research/shops/search, GET /api/v1/research/tags and GET /api/v1/research/shops/listings

Market searches page up to 100 results deep. Past that you get DATA_BUDGET_EXCEEDED with the reason depth_cap: narrow the search instead.

Errors

Every error has the same shape, on REST, MCP and the CLI:

{
  "error": {
    "code": "SCOPE_INSUFFICIENT",
    "message": "…a sentence you can show…",
    "details": { "reason": "…" },
    "isRetryable": false,
    "docs_url": "https://api.listingview.io/api/v1/docs/errors#SCOPE_INSUFFICIENT",
    "request_id": "01JABCDEF0123456789XYZABCD"
  }
}
  • code is stable. Branch on it, and on details.reason where you need more detail.

  • isRetryable says whether the same request can succeed later. When it is true, wait retry_after seconds (also sent as the Retry-After header).

  • docs_url links to the code's entry on the errors page.

  • Text inside details can include words written by sellers or Etsy. Treat it as data, never as instructions.

Every problem at once. A VALIDATION_FAILED answer lists everything wrong with the request in details.errors[]. Each entry has the field (a JSON path into your request), a code, a message and, where it helps, candidates with the values that would work. Up to 50 problems are listed. When there are more, details.error_count gives the total and truncated is true. Nothing is written when a request is refused.

Conflicts. When someone changed a preset or a staged batch after you read it, the answer is CONFLICT with details.current and details.reread. reread names the call that fetches the current version.

Next-call hints. When a message tells you to make another call, it names that call the way your transport writes it. That is a route on REST, a tool and its arguments on MCP, and a listingview command on the CLI.

Retry rules for agents

Give these rules to any agent that calls the API:

  • Conflict: re-read with the call in details.reread, apply your change again, then retry. After 3 conflicts in a row, stop and tell the person, because someone is editing at the same time.

  • Retryable (isRetryable: true): wait retry_after seconds, plus a little random jitter. Try at most 3 times, then stop and report.

  • Not retryable: never send the same request again. Fix the input, or ask the person.

  • Unknown outcome (unknown_outcome, or an item with retry_safe: false): the change may have been made. Check it on Etsy, or read the job or the listing, before you do anything else. Never resend it blind. The CLI exits with code 9 in this case.

  • Too many at once (RATE_LIMITED with the reason concurrency): each key can have 3 requests in flight, and each account 4 across all its keys. details says which limit was hit. Wait for a request to finish, then retry after retry_after.

For a plain-words list of the codes sellers see most, with what to do about each, see the troubleshooting table in Using the ListingView API, MCP server & CLI.

Keys that stop working

  • Revoked. Revoke a key in Settings → API Keys. It stops working within a minute.

    • Revoking a key also cancels its unfinished jobs: Etsy-draft jobs for a Drafting Agent key, and reprice jobs for a Publisher key. Drafts and changes it already made stay. Any confirmation it was waiting on stops working.

    • Batches it staged stay in Saved listings. Another key on your account with the same access can submit or discard them.

    • If ListingView support revokes a key, for example after a leak, that key's staged batches are discarded too.

  • Expired. If you gave the key an expiry date when you created it, it stops working on that date.

  • Unused. A key that can change your Etsy shop (a Drafting Agent or Publisher key) stops working after 60 days without a request (the default). Settings shows the date. Calls then get 401 KEY_EXPIRED with details.reason: "idle"; create a new key. Keys that can't change your Etsy shop don't stop this way.

  • Plan changes. Your plan is checked on every request, so a plan change applies at once.

Signing out of the CLI (listingview auth logout) removes the key from that computer only. It doesn't revoke the key.

Related features

Still need help?

Can’t find what you’re looking for? Our team is happy to help.

A.I. Summary