Spree Commerce Pricing API
Prices and price lists for currency-, market-, and customer-group-specific pricing
Prices and price lists for currency-, market-, and customer-group-specific pricing
openapi: 3.0.3
info:
title: Admin Account / Address Pricing API
contact:
name: Spree Commerce
url: https://spreecommerce.org
email: hello@spreecommerce.org
description: "Spree Admin API v3 - Administrative API for managing products, orders, and store settings.\n\n## Authentication\n\nThe Admin API requires a secret API key passed in the `x-spree-api-key` header.\nSecret API keys can be generated in the Spree admin dashboard.\n\n## Response Format\n\nAll responses are JSON. List endpoints return paginated responses with `data` and `meta` keys.\nSingle resource endpoints return a flat JSON object.\n\n## Resource IDs\n\nEvery resource is identified by an opaque string ID (e.g. `prod_86Rf07xd4z`,\n`variant_k5nR8xLq`, `or_UkLWZg9DAJ`). Use these IDs everywhere — URL paths,\nrequest bodies, and Ransack filters all accept them directly.\n\n## Error Handling\n\nErrors return a consistent format:\n```json\n{\n \"error\": {\n \"code\": \"validation_error\",\n \"message\": \"Validation failed\",\n \"details\": { \"name\": [\"can't be blank\"] }\n }\n}\n```\n"
version: v3
servers:
- url: http://{defaultHost}
variables:
defaultHost:
default: localhost:3000
tags:
- name: Pricing
description: Prices and price lists for currency-, market-, and customer-group-specific pricing
paths:
/api/v3/admin/price_lists:
get:
summary: List price lists
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Returns the price lists configured for the current store.
**Required scope:** `read_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: page
in: query
required: false
schema:
type: integer
- name: limit
in: query
required: false
schema:
type: integer
- name: q[name_cont]
in: query
required: false
schema:
type: string
- name: q[status_eq]
in: query
required: false
schema:
type: string
- name: sort
in: query
required: false
schema:
type: string
- name: expand
in: query
required: false
description: 'Comma-separated associations to embed. Supported: `price_rules`.'
schema:
type: string
responses:
'200':
description: price lists found
content:
application/json:
example:
data:
- id: pl_UkLWZg9DAJ
name: Wholesale
description: null
status: draft
position: 1
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:52.444Z'
updated_at: '2026-06-12T17:24:52.444Z'
currently_active: false
products_count: 0
prices_count: 0
product_ids: []
- id: pl_gbHJdmfrXB
name: Holiday
description: null
status: draft
position: 2
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:52.447Z'
updated_at: '2026-06-12T17:24:52.447Z'
currently_active: false
products_count: 0
prices_count: 0
product_ids: []
meta:
page: 1
limit: 25
count: 2
pages: 1
from: 1
to: 2
in: 2
previous: null
next: null
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/PriceList'
meta:
$ref: '#/components/schemas/PaginationMeta'
required:
- data
- meta
post:
summary: Create a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Creates a new draft price list.
**Required scope:** `write_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: price list created (server-to-server — rules + prices, no product_ids)
content:
application/json:
example:
id: pl_EfhxLZ9ck8
name: EU wholesale
description: null
status: draft
position: 3
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:53.499Z'
updated_at: '2026-06-12T17:24:53.501Z'
currently_active: false
products_count: 1
prices_count: 2
product_ids:
- prod_UkLWZg9DAJ
schema:
$ref: '#/components/schemas/PriceList'
'422':
description: validation error
content:
application/json:
example:
error:
code: validation_error
message: Name can't be blank
details:
name:
- can't be blank
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: EU wholesale
description:
type: string
nullable: true
starts_at:
type: string
nullable: true
example: '2026-06-01T00:00:00Z'
ends_at:
type: string
nullable: true
example: '2026-09-01T00:00:00Z'
match_policy:
type: string
enum:
- all
- any
example: all
position:
type: integer
example: 1
product_ids:
type: array
items:
type: string
description: Prefixed product ids to seed the list with.
example:
- prod_aBc123
rules:
type: array
description: STI-typed price rules to attach on create. Existing rules on the same payload via PATCH reconcile by id.
items:
type: object
required:
- type
properties:
type:
type: string
example: volume_rule
preferences:
type: object
additionalProperties: true
prices:
type: array
description: 'Server-to-server alternative to `product_ids`: ship the exact per-variant prices the list should contain. Each row upserts on the unique key `(variant_id, currency, price_list_id)`. Mix-and-match with `product_ids` is supported but typically unnecessary — `prices` alone tells the server which variants belong to the list and what the override amount is.'
items:
type: object
required:
- variant_id
- currency
properties:
variant_id:
type: string
example: variant_xY9
currency:
type: string
example: USD
amount:
type: string
nullable: true
example: '19.99'
compare_at_amount:
type: string
nullable: true
example: '24.99'
/api/v3/admin/price_lists/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Get a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `read_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: expand
in: query
required: false
schema:
type: string
responses:
'200':
description: price list found
content:
application/json:
example:
id: pl_UkLWZg9DAJ
name: Wholesale
description: null
status: draft
position: 1
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:53.806Z'
updated_at: '2026-06-12T17:24:53.806Z'
currently_active: false
products_count: 0
prices_count: 0
product_ids: []
schema:
$ref: '#/components/schemas/PriceList'
'404':
description: price list not found
content:
application/json:
example:
error:
code: record_not_found
message: Price list not found
schema:
$ref: '#/components/schemas/ErrorResponse'
patch:
summary: Update a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Updates a price list. The optional `rules:` array reconciles nested
STI-typed price rules in a single round-trip — existing rules update
by `id`, new rules build, missing rules destroy. Mirrors the
promotion editor''s "save the whole thing on Save" pattern.
**Required scope:** `write_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: price list updated with nested rules
content:
application/json:
example:
id: pl_UkLWZg9DAJ
name: Wholesale (Q3)
description: null
status: draft
position: 1
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:55.427Z'
updated_at: '2026-06-12T17:24:55.712Z'
currently_active: false
products_count: 0
prices_count: 0
product_ids: []
schema:
$ref: '#/components/schemas/PriceList'
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
description:
type: string
nullable: true
starts_at:
type: string
nullable: true
ends_at:
type: string
nullable: true
match_policy:
type: string
enum:
- all
- any
position:
type: integer
product_ids:
type: array
items:
type: string
description: Prefixed product ids — reconciles list membership (adds + removes).
example:
- prod_aBc123
rules:
type: array
items:
type: object
required:
- type
properties:
id:
type: string
nullable: true
type:
type: string
example: volume_rule
preferences:
type: object
additionalProperties: true
prices:
type: array
description: Individual price overrides (the spreadsheet payload). Each row updates by `id` if shipped, otherwise upserts on the unique key `(variant_id, currency, price_list_id)`.
items:
type: object
oneOf:
- required:
- id
- required:
- variant_id
- currency
properties:
id:
type: string
example: price_aBc123
variant_id:
type: string
example: variant_xY9
currency:
type: string
example: USD
amount:
type: string
nullable: true
example: '12.50'
compare_at_amount:
type: string
nullable: true
example: '15.00'
delete:
summary: Delete a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Soft-deletes the price list. Associated prices are removed asynchronously.
**Required scope:** `write_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'204':
description: price list deleted
/api/v3/admin/price_lists/{id}/activate:
parameters:
- name: id
in: path
required: true
schema:
type: string
patch:
summary: Activate a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Transitions a draft / inactive list to `active`. If `starts_at` is
in the future the list is marked `scheduled` instead, matching the
legacy admin behaviour.
**Required scope:** `write_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: price list activated
content:
application/json:
example:
id: pl_UkLWZg9DAJ
name: Wholesale
description: null
status: active
position: 1
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:56.035Z'
updated_at: '2026-06-12T17:24:56.320Z'
currently_active: true
products_count: 0
prices_count: 0
product_ids: []
schema:
$ref: '#/components/schemas/PriceList'
/api/v3/admin/price_lists/{id}/deactivate:
parameters:
- name: id
in: path
required: true
schema:
type: string
patch:
summary: Deactivate a price list
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_products` (for API-key authentication).'
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: price list deactivated
content:
application/json:
example:
id: pl_UkLWZg9DAJ
name: Price List 1
description: null
status: inactive
position: 1
match_policy: all
starts_at: null
ends_at: null
deleted_at: null
created_at: '2026-06-12T17:24:56.329Z'
updated_at: '2026-06-12T17:24:56.617Z'
currently_active: false
products_count: 0
prices_count: 0
product_ids: []
schema:
$ref: '#/components/schemas/PriceList'
/api/v3/admin/prices:
get:
summary: List prices
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Generic prices endpoint covering both base prices and price-list
overrides. Filter with Ransack: `q[price_list_id_eq]=…`,
`q[currency_eq]=USD`, `q[price_list_id_null]=true` (base prices only).
The admin spreadsheet uses this with server-side pagination so it
scales past the metadata-PATCH path on `/price_lists/:id`.
**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { data: prices } = await client.prices.list({\n price_list_id_eq: 'pl_xxx',\n currency_eq: 'USD',\n expand: ['variant'],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
- name: page
in: query
required: false
schema:
type: integer
- name: limit
in: query
required: false
schema:
type: integer
- name: q[price_list_id_eq]
in: query
required: false
schema:
type: string
- name: q[price_list_id_null]
in: query
required: false
schema:
type: boolean
- name: q[currency_eq]
in: query
required: false
schema:
type: string
- name: q[variant_id_eq]
in: query
required: false
schema:
type: string
- name: sort
in: query
required: false
description: Comma-separated sort keys. Supports e.g. `variant_product_name,variant_id`.
schema:
type: string
- name: expand
in: query
required: false
description: 'Comma-separated associations to embed. Supported: `variant`.'
schema:
type: string
responses:
'200':
description: prices found
content:
application/json:
example:
data:
- id: price_gbHJdmfrXB
amount: '19.99'
amount_in_cents: 1999
compare_at_amount: null
compare_at_amount_in_cents: null
currency: USD
display_amount: $19.99
display_compare_at_amount: null
price_list_id: null
variant_id: variant_gbHJdmfrXB
created_at: '2026-06-12T17:24:56.681Z'
updated_at: '2026-06-12T17:24:56.681Z'
- id: price_EfhxLZ9ck8
amount: '5.0'
amount_in_cents: 500
compare_at_amount: null
compare_at_amount_in_cents: null
currency: USD
display_amount: $5.00
display_compare_at_amount: null
price_list_id: pl_UkLWZg9DAJ
variant_id: variant_gbHJdmfrXB
created_at: '2026-06-12T17:24:56.685Z'
updated_at: '2026-06-12T17:24:56.685Z'
meta:
page: 1
limit: 25
count: 2
pages: 1
from: 1
to: 2
in: 2
previous: null
next: null
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Price'
meta:
$ref: '#/components/schemas/PaginationMeta'
required:
- data
- meta
post:
summary: Create a price
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Creates a single price. Omit `price_list_id` to create a base price.
For more than a handful of rows, prefer `POST /admin/prices/bulk_upsert`.
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst price = await client.prices.create({\n variant_id: 'variant_xxx',\n currency: 'USD',\n amount: '19.99',\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'201':
description: price created
content:
application/json:
example:
id: price_uw2YK1rnl0
amount: '9.99'
amount_in_cents: 999
compare_at_amount: null
compare_at_amount_in_cents: null
currency: EUR
display_amount: €9.99
display_compare_at_amount: null
price_list_id: null
variant_id: variant_EfhxLZ9ck8
created_at: '2026-06-12T17:24:57.494Z'
updated_at: '2026-06-12T17:24:57.494Z'
schema:
$ref: '#/components/schemas/Price'
requestBody:
content:
application/json:
schema:
type: object
required:
- variant_id
- currency
properties:
variant_id:
type: string
example: variant_xY9
currency:
type: string
example: USD
amount:
type: string
nullable: true
example: '19.99'
compare_at_amount:
type: string
nullable: true
example: '24.99'
price_list_id:
type: string
nullable: true
example: pl_aBc123
/api/v3/admin/prices/{id}:
parameters:
- name: id
in: path
required: true
schema:
type: string
get:
summary: Get a price
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `read_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst price = await client.prices.get('price_xxx')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: price found
content:
application/json:
example:
id: price_EfhxLZ9ck8
amount: '5.0'
amount_in_cents: 500
compare_at_amount: null
compare_at_amount_in_cents: null
currency: USD
display_amount: $5.00
display_compare_at_amount: null
price_list_id: pl_UkLWZg9DAJ
variant_id: variant_gbHJdmfrXB
created_at: '2026-06-12T17:24:57.574Z'
updated_at: '2026-06-12T17:24:57.574Z'
schema:
$ref: '#/components/schemas/Price'
'404':
description: price not found
content:
application/json:
example:
error:
code: record_not_found
message: Price not found
schema:
$ref: '#/components/schemas/ErrorResponse'
patch:
summary: Update a price
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: '**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst price = await client.prices.update('price_xxx', {\n amount: '12.34',\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: price updated
content:
application/json:
example:
id: price_EfhxLZ9ck8
amount: '12.34'
amount_in_cents: 1234
compare_at_amount: null
compare_at_amount_in_cents: null
currency: USD
display_amount: $12.34
display_compare_at_amount: null
price_list_id: pl_UkLWZg9DAJ
variant_id: variant_gbHJdmfrXB
created_at: '2026-06-12T17:24:58.283Z'
updated_at: '2026-06-12T17:24:58.593Z'
schema:
$ref: '#/components/schemas/Price'
requestBody:
content:
application/json:
schema:
type: object
properties:
amount:
type: string
nullable: true
compare_at_amount:
type: string
nullable: true
delete:
summary: Delete a price
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: 'Soft-deletes the price (acts_as_paranoid).
**Required scope:** `write_products` (for API-key authentication).'
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nawait client.prices.delete('price_xxx')"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'204':
description: price deleted
/api/v3/admin/prices/bulk_upsert:
post:
summary: Bulk-upsert prices
tags:
- Pricing
security:
- api_key: []
bearer_auth: []
description: "Upserts a batch of prices in a single SQL round trip.\n\nEach row either:\n* targets an existing price by `id`, OR\n* matches on the unique key `(variant_id, currency, price_list_id)` —\n updating the existing row if one exists, creating one otherwise.\n\nModel callbacks (e.g. PriceHistory) are bypassed; this is a\nbulk-write fast path for the admin spreadsheet. The response\ncarries `price_count` — the number of rows touched.\n\n\n**Required scope:** `write_products` (for API-key authentication)."
x-codeSamples:
- lang: javascript
label: Spree Admin SDK
source: "import { createAdminClient } from '@spree/admin-sdk'\n\nconst client = createAdminClient({\n baseUrl: 'https://your-store.com',\n secretKey: 'sk_xxx',\n})\n\nconst { price_count } = await client.prices.bulkUpsert({\n prices: [\n {\n variant_id: 'variant_xxx',\n currency: 'USD',\n price_list_id: 'pl_xxx',\n amount: '11.11',\n },\n {\n variant_id: 'variant_yyy',\n currency: 'USD',\n price_list_id: 'pl_xxx',\n amount: '22.22',\n },\n ],\n})"
parameters:
- name: x-spree-api-key
in: header
required: true
schema:
type: string
- name: Authorization
in: header
required: true
schema:
type: string
responses:
'200':
description: prices upserted
content:
application/json:
example:
price_count: 2
schema:
type: object
properties:
price_count:
type: integer
'422':
description: missing prices key
content:
application/json:
example:
error:
code: missing_prices
message: prices is required (send an empty array to no-op).
schema:
$ref: '#/components/schemas/ErrorResponse'
requestBody:
content:
application/json:
schema:
type: object
required:
- prices
properties:
prices:
type: array
items:
type: object
required:
- variant_id
- currency
properties:
id:
type: string
nullable: true
example: price_aBc123
variant_id:
typ
# --- truncated at 32 KB (87 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/spree/refs/heads/main/openapi/spree-pricing-api-openapi.yml