For store developers

Merchant API · asynchronous ingestion V2

This is the complete public integration contract for sending official store offers to PriceInfo. The stable HTTP namespace remains /api/merchant/v1; V2 names the asynchronous ingestion pipeline, so existing integrations do not need a breaking URL migration.

Quickstart

From API key to the first completed import

  1. Submit the store application. After approval, keep the issued API key only in server-side secret storage.
  2. Call GET /account and confirm that the target country appears in data.markets.
  3. Send a unique logical batch to POST /offers/bulk with a stable Idempotency-Key.
  4. On 202, store data.id. A successful HTTP response does not mean rows are already published.
  5. Poll GET /imports/{job} until a terminal status, then read /errors when needed.
Copyable request

Queue a direct offer batch

curl --request POST \
  https://priceinfo.eu/api/merchant/v1/offers/bulk \
  --header "Authorization: Bearer pi_live_xxx" \
  --header "Idempotency-Key: catalog-cz-2026-08-08-001" \
  --header "Accept: application/json" \
  --header "Content-Type: application/json" \
  --data '{
    "country": "CZ",
    "items": [
      {
        "name": "Apple iPhone 15 128GB Black",
        "url": "https://merchant.example/iphone-15-black",
        "price": 18490,
        "currency": "CZK",
        "brand": "Apple",
        "model": "iPhone 15 128GB",
        "gtin": "195949036545",
        "mpn": "MTP03ZD/A",
        "availability": "in stock",
        "delivery_price": 79,
        "delivery_time": "1-2 days"
      }
    ]
  }'
Reference

Endpoint summary

MethodPathPurpose
GET/accountVerify the API key and read the merchant, enabled markets, default currencies, and counters.
GET/feedsList hosted feed configurations. Supports page and per_page.
POST/feedsCreate a pending hosted XML, CSV, or JSON feed configuration.
GET/feeds/{feed}Read one merchant-owned feed configuration.
PATCH/feeds/{feed}Update a feed URL, format, mapping, schedule, market, or status.
POST/feeds/{feed}/importQueue a hosted feed import.
POST/offersQueue one offer update through async ingestion V2.
POST/offers/bulkQueue 1–100 offer updates through async ingestion V2.
GET/importsList import jobs. Supports page and per_page.
GET/imports/{job}Poll a merchant-owned import job and its counters.
GET/imports/{job}/errorsList row-level validation or import errors.

All {feed} and {job} lookups are merchant-scoped. A record owned by another merchant returns 404.

Direct pushes

Offer request fields

POST /offers accepts one item with country on the item. POST /offers/bulk accepts country once at request level and an items array containing 1–100 items.

FieldTypeLocationRule
countrystring(2)Bulk request or single itemRequired. ISO 3166-1 alpha-2 market enabled for the merchant.
actionupsert | offlineItemOptional; defaults to upsert. Offline still requires the item identity fields.
namestring ≤ 500ItemRequired. Store-facing product name.
urlURL ≤ 2048ItemRequired. Public product page in the merchant store.
pricenumber ≥ 0ItemRequired. Current price including tax where applicable.
currencystring(3)ItemOptional ISO 4217 code; the market currency is used when omitted.
old_pricenumber ≥ 0ItemOptional previous or reference price.
categorystring ≤ 500ItemOptional merchant category path.
brandstring ≤ 255ItemStrongly recommended for matching.
modelstring ≤ 255ItemRecommended model name.
gtinstring ≤ 32ItemStrongly recommended EAN/UPC/GTIN identifier.
mpnstring ≤ 255ItemRecommended manufacturer part number.
image_urlURL ≤ 2048ItemOptional public product image URL.
availabilitystring ≤ 120ItemOptional readable value such as in stock, out of stock, preorder, or unknown.
delivery_pricenumber ≥ 0ItemOptional delivery price.
delivery_timestring ≤ 120ItemOptional readable estimate such as 1-2 days.
_metaobjectItemOptional integration metadata. cms, opencart_product_id, and country are accepted.

A valid offer is not automatically a new public product. Exact identifiers are matched first; uncertain identities increment review_records and remain unavailable for publication until reviewed.

Asynchronous ingestion V2

202, idempotency, and polling

Direct pushes return 202 Accepted. Generate one stable idempotency key for each logical batch and retain it until the request result is known. Repeating the same merchant request with the same key returns the original job and sets meta.idempotency_replay to true instead of inserting the batch twice.

Poll direct jobs every 2–10 seconds with exponential backoff. Poll hosted feed jobs every 10–30 seconds. Stop at completed, completed_with_errors, or failed. Treat review_records as waiting for PriceInfo review, not as a transport error.

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "data": {
    "id": 9002,
    "type": "merchant_direct_api",
    "source": "direct_api",
    "status": "running",
    "pipeline_version": "v2",
    "counters": {
      "total_records": 1,
      "processed_records": 0,
      "created_records": 0,
      "updated_records": 0,
      "review_records": 0,
      "rejected_records": 0,
      "error_count": 0
    }
  },
  "meta": {
    "accepted": true,
    "idempotency_replay": false
  }
}

Import status values

waiting_capacity

Accepted and waiting for per-merchant or global ingestion capacity. Continue polling.

queued

Accepted and queued for preparation. Continue polling.

preparing

The hosted source is being downloaded, verified, and split into bounded chunks.

running

One or more chunks are being processed.

completed

Terminal success. All accepted rows were processed without rejected rows.

completed_with_errors

Terminal result. Read counters and /errors; successful rows remain applied.

failed

Terminal failure. Inspect the job, correct the source or request, and submit a new logical batch.

Job counters are total_records, processed_records, created_records, updated_records, review_records, rejected_records, and error_count.

Hosted feeds

XML, CSV, or JSON catalog URL

Create a feed with name, two-letter country, format, and a public HTTPS feed_url. Optional fields are mapping_preset, field_mapping_json, and schedule_cron. New feeds always start as pending; they can become active after a completed validation import.

Supported presets are priceinfo, salidzini_like, heureka_like, ceneo_like, and approved custom mappings. Use Heureka-like for common CZ/SK exports and Ceneo-like for common PL exports.

POST /feeds
{
  "name": "Main CZ Heureka XML feed",
  "country": "CZ",
  "format": "xml",
  "feed_url": "https://merchant.example/heureka.xml",
  "mapping_preset": "heureka_like",
  "schedule_cron": "0 * * * *"
}
Failure handling

HTTP statuses and retry decisions

StatusMeaning and client action
200Read succeeded.
201Hosted feed configuration created.
202Import accepted. Save data.id and poll the job.
401Bearer token is missing, inactive, expired, or belongs to an inactive merchant.
404The feed or job does not exist or belongs to another merchant.
409The hosted feed already has an active import; poll the returned job.
422Validation failed or the merchant is not enabled for the requested market.
429Quota exceeded. Wait for the Retry-After duration before retrying.
500Unexpected server error. Retry later with exponential backoff.

Laravel validation responses contain message and an errors object keyed by field. Import row errors contain severity, record_reference, error_code, message, and raw_payload. Do not retry unchanged 422 rows. For 429, obey the Retry-After response header.

Default limits

Request and item quotas

ScopePer minutePer hourItems per dayMax items/request
feeds.import3 requests20 requests
offers.store60 requests1,000 requests5,0001
offers.bulk20 requests200 requests25,000100

These are contract defaults and may be adjusted for an approved merchant plan. A 429 response reports the exceeded scope, limit, reset time, and retry delay.