Overview
API integrations allow you to connect PXM with external systems such as ERPs, ecommerce platforms, or custom applications. Instead of manually updating data, APIs enable systems to communicate automatically -- saving time, reducing errors, and keeping your product content in sync.
The PXM API is a RESTful HTTP API using JSON for request and response bodies. It supports operations on collections (products), files (digital assets), categories (navigation folders), attributes (metadata fields), and more.
Base URL: https://entapi.amplifi.io
Most API endpoints are versioned under /v2.1/. The search endpoints also support /v2/ and /v3/. See the API Reference below for details.
Data Model & Object Relationships
Understanding how PXM objects relate to each other is essential before building any integration.
Basic PXM Hierarchy
Category Folder
|-- Sub-Category Folder (nested via parent_id)
| |-- Collection / Product
| |-- File / Asset (image, PDF, video)
| |-- Attribute Values (product metadata)
|
|-- Collection / Product
|-- File / Asset
Categories organize collections (products) into folder structures for navigation. Files are media assets linked to collections. A collection can be associated with one or more categories depending on its parent/direct_parent configuration. Files can also be associated with multiple collections.
Product Variant Hierarchy
Parent Collection (collection_type = "parent") |-- Variant Collection (collection_type = "variant", parent_topic_id = parent.id) |-- Variant Collection (collection_type = "variant", parent_topic_id = parent.id) |-- Variant Collection (collection_type = "variant", parent_topic_id = parent.id)
The variant hierarchy is separate from the category hierarchy. collection_type designates whether a collection is a parent or variant. parent_topic_id links each variant back to its parent collection. A standalone product omits collection_type entirely.
Full Account Structure
Account
|-- Regions (Org Units) -- localization scopes (e.g. US, EU, global)
|-- Attributes -- global metadata field definitions
|-- Categories -- folder structure for organizing content
| |-- Sub-categories (nested via parent_id)
|-- Collections -- product records
|-- Attribute values (metadata on this product)
|-- Files (linked digital assets: images, PDFs, video)
|-- Variant Collections (collection_type='variant', linked via parent_topic_id)
A Collection is the core product record. It can stand alone, act as a parent (product family), or be a variant (a specific SKU). Collections are placed inside Categories for navigation, linked to Files for media, and carry Attribute values for product data.
Key relationship fields and what they do:
Field | On Object | What it does |
| Collection or Category | Places this object inside a parent Category folder |
| Collection | Array of Category IDs -- associates the collection with those categories |
| Collection |
|
| Collection (variant only) | ID of the parent collection this variant belongs to -- required when collection_type is variant |
| Collection, File | Scopes this object to specific regions. Use GET /region to find IDs. If omitted, defaults to the account primary region. |
Real-World API Behavior -- Read This First
Before building your integration, be aware of the following known behaviors. They are not bugs -- they are characteristics of the API that every integration needs to handle:
Response shapes are not uniform across endpoints. Some list endpoints return a raw JSON array. Others wrap results in a keyed object. Your code must handle both. See the Response Format Variations section below.
The Swagger documentation and actual runtime behavior sometimes differ. Some fields marked readOnly in the spec are writable in practice. Test against the live API when in doubt.
PUT requests fully replace array fields. If you PUT collections with one ID, all previously linked collections are removed. Always GET first, merge, then PUT. See Understanding PUT Behavior below.
Access tokens are long-lived (1 year). This is unusual for OAuth. Store tokens securely and treat them like passwords.
Before You Begin
Before using the API, you'll need:
1. A PXM account with API access enabled
2. A Client ID and Client Secret (generated in PXM settings)
3. Basic understanding of REST APIs and JSON
Creating an API Authorization
To generate your API credentials in PXM:
1. Log in to PXM and navigate to Settings
2. Go to Integrations → API
3. Click Create New Integration
4. Give your integration a name and save
5. You may now use the Client ID and Client Secret
6. The Client ID and Client Secret are tied to the individual user. Deactivation of the user will deactivate the Client ID and Client Secret.
Authentication & Getting a Token
The PXM API uses the OAuth 2.0 Client Credentials flow. You exchange your Client ID and Client Secret for a Bearer token included on every subsequent API request.
Endpoint: POST https://entapi.amplifi.io/v2.1/oauth/authorize
Headers:
Authorization: Basic <base64(client_id:client_secret)>Content-Type: application/json
Request Body:
{"grant_type": "client_credentials"}
Python Example (Option A -- recommended):
import requestsAUTH_URL = "https://entapi.amplifi.io/v2.1/oauth/authorize"BASE_URL = "https://entapi.amplifi.io/v2.1"resp = requests.post( AUTH_URL, auth=(client_id, client_secret), json={"grant_type": "client_credentials"})token = resp.json()["access_token"]headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
Python Example (Option B -- manual Base64):
import requests, base64credentials = base64.b64encode(f"{client_id}:{client_secret}".encode("utf-8")).decode("utf-8")resp = requests.post( "https://entapi.amplifi.io/v2.1/oauth/authorize", headers={ "Authorization": f"Basic {credentials}", "Content-Type": "application/json", }, json={"grant_type": "client_credentials"},)token = resp.json()["access_token"]
Response:
{ "access_token": "eyJhb...", "token_type": "Bearer", "expires_in": "31536000"}
The expires_in value is 31,536,000 seconds (1 year). Tokens are long-lived -- cache and reuse them rather than generating a new one per request.
Include the token on every API call:
Authorization: Bearer <your_token>
Token Security
* Treat your token like a password -- never expose it in client-side code, logs, or version control
* Store it in environment variables or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault)
* Rotate tokens proactively before expiry rather than waiting for them to expire in production
* If a token is compromised, revoke it from PXM Settings immediately and generate a new one
Accessing the PXM API Documentation
The full interactive API documentation (Swagger UI) is available at:
Note: The Swagger spec is a useful reference for endpoint structure and field names, but some runtime behaviors differ -- for example, some fields marked readOnly are writable in practice. Treat it as a starting point and validate against the live API when in doubt.
Key Concepts
Concept | What It Is |
Collection | A product record in PXM. Holds attribute values, linked files, and metadata. Can be standalone, a |
File | A digital asset (image, video, PDF, etc.) managed in PXM's DAM. Files are uploaded via a two-step presigned URL process and can be linked to collections. |
Category | A folder in PXM used to organize collections and files. Categories support nested hierarchies via |
Attribute | A metadata field attached to a collection, file, or category. Attributes have typed values: Text, Date, List, Pick List, Boolean, or Number. |
Attribute Group | A named grouping of attributes on a collection, used to organize related metadata fields together. |
Region | A localization scope in PXM. Content can be scoped to specific regions. Omitting |
Resource | A URL-based reference (document, link, spec sheet) that can be attached to a category or collection. |
Response Format Variations
Different endpoints return data in different shapes. Do not assume every endpoint returns the same wrapper format -- inspect each endpoint's documented response shape and write your parser accordingly.
Endpoint | Response Shape | How to Access Items |
| Raw JSON array |
|
|
|
|
| Raw JSON array |
|
|
|
|
GET /v2.1/category -- returns a raw JSON array:
[
{
"id": "cat_abc123",
"name": "Apparel",
"parent_id": null
},
{
"id": "cat_def456",
"name": "Jackets",
"parent_id": "cat_abc123"
}
]
GET /v2.1/collection -- returns an object with a collections key:
{
"collections": [
{
"id": "col_ghi789",
"name": "Women's Rain Jacket",
"parent_id": "cat_def456"
}
]
}
GET /v2.1/file -- returns a raw JSON array:
[
{
"id": "file_jkl012",
"file_name": "rain-jacket-hero.jpg",
"collections": [
{ "id": "col_ghi789" }
]
}
]
GET /v3/folder/search -- returns an object with hits and total:
{
"hits": [
{ "id": "col_ghi789", "name": "Women's Rain Jacket" }
],
"total": 1
}
Defensive pagination helper (handles all response shapes):
page = resp.json()if isinstance(page, list): items = pageelif isinstance(page, dict): items = ( page.get("collections") or page.get("files") or page.get("items") or page.get("data") or [] )else: items = []
API Reference
All endpoints use https://entapi.amplifi.io as the base URL. Most are versioned at /v2.1/. Include your Bearer token in every request.
Categories
Categories are folder structures used to organize collections and files in PXM. They support nested hierarchies.
Method | Endpoint | Description |
|
| List all categories |
|
| Create a category |
|
| Get category by ID |
|
| Update a category |
|
| Delete a category |
|
| Get files in a category |
To create a category, only name is required. Use parent_id to nest it inside another category.
{ "name": "Apparel", "parent_id": "parent-category-uuid"}
Collections (Products)
Collections are product records in PXM. They hold attribute values, linked files, and product metadata. A collection can be a standalone product, a parent (product family), or a variant (specific SKU).
Method | Endpoint | Description |
|
| List all collections |
|
| Create a collection |
|
| Get a collection by ID |
|
| Update a collection |
|
| Delete a collection |
|
| Get files linked to a collection |
|
| Get image variants (filter by size, file_type, stack_ids) |
|
| Get related collections (association groups) |
List Parameters (GET /v2.1/collection)
Parameter | Type | Description |
| array | Comma-separated collection IDs to fetch |
| array | Filter results to collections scoped to these regions |
| integer | Records per page |
| integer | Records to skip |
| string | Filter by parent category or collection ID |
| string |
|
| boolean | Return a trimmed response -- useful for large catalogs |
| boolean | Return only metadata, no attribute values |
CREATING a Collection -- POST /v2.1/collection
Only name is required. All other fields are optional on create.
{ "name": "Blue Rain Jacket", "additional_title": "SKU-BRJ-001", "parent_id": "category-uuid-here", "published": true, "published_start_date": "2025-01-01T00:00:00Z"}
To create a parent collection (product family):
{ "name": "Rain Jacket Collection", "collection_type": "parent"}
To create a variant linked to a parent:
{ "name": "Blue Rain Jacket - Size M", "collection_type": "variant", "parent_topic_id": "parent-collection-uuid"}
UPDATING a Collection -- PUT /v2.1/collection/{id}
Only include the fields you want to change. You do not need to send the full object.
⚠️ Array fields are fully replaced: direct_parent and attributes replace their entire previous values on every PUT. Always GET the current state first, merge your changes, and then PUT the full merged value.
{ "name": "Updated Product Name", "published": true, "published_start_date": "2025-11-01T00:00:00Z", "direct_parent": ["category-uuid-1", "category-uuid-2"], "attributes": [ { "id": "attribute-uuid", "type": "update", "value": "New Value" } ]}
Collection Fields Reference
Field | Type | Create | Update | Description |
| string | Required | Optional | Product/collection name |
| string | Optional | Optional | Secondary or alternate title |
| string | Optional | Optional | Primary parent Category ID |
| array | Optional | Optional (full replace) | Array of Category IDs to associate with |
| boolean | Optional | Optional | Whether the collection is published |
| date-time | If published=true | If published=true | Format: |
| date-time | Optional | Optional | Format: |
| string | Optional | Optional | Omit for standalone. |
| string | If variant | If variant | ID of parent collection -- required when collection_type is variant |
| array | Optional | Optional (full replace) | Array of attribute ops -- each with |
| array | Optional | Optional | Region UUIDs to scope this collection. Omit = default/primary region. |
Files (Digital Assets)
Files are digital assets -- images, PDFs, videos, and other media -- stored in PXM's Digital Asset Manager (DAM). Files are uploaded via a presigned URL flow and can be linked to one or more collections.
Method | Endpoint | Description |
|
| List all files |
|
| Get file by ID |
|
| Update file metadata |
|
| Request presigned upload URL |
|
| Confirm and finalize upload |
|
| Search files by filename |
List Parameters (GET /v2.1/file)
Parameter | Type | Description |
| array | Return only files linked to these collections |
| array | Scope results to files in these regions |
| integer | Number of files per page |
| integer | Records to skip (for pagination) |
| date | Filter files created on or after this date (YYYY-MM-DD) |
| date | Filter files created on or before this date (YYYY-MM-DD) |
| date | Filter files updated on or after this date (YYYY-MM-DD) |
| date | Filter files updated on or before this date (YYYY-MM-DD) |
| boolean | Return a trimmed response -- useful when listing many files |
File Upload Lifecycle
Uploading a file to PXM is a multi-step process:
1. Request Upload -- Call POST /v2.1/file/request-upload to get a presigned S3 URL
{ "filename": "product-hero.jpg", "file_name": "product-hero.jpg", "mime_type": "image/jpeg", "size": 2048000}
Note: Send both filename and file_name for compatibility across tenant configurations.
2. Check Upload Type -- The response includes upload_type: either standard (single PUT) or multipart (multiple parts for large files)
3. Upload to S3 (Single) -- If upload_type == "standard", PUT the file bytes directly to the presigned URL
4. Upload to S3 (Multipart) -- If upload_type == "multipart": upload each part to its presigned URL, capture the ETag header from each response, then confirm with the upload_id and array of parts
5. Confirm Upload -- Call POST /v2.1/file/confirm-upload to register the file in PXM:
{ "file_path": "s3path/temp/upl_abc123/product-hero.jpg", "file_name": "product-hero.jpg", "file_size": 1024000, "metadata": { "published": true, "published_start_date": "2025-01-01T00:00:00Z", "region_ids": ["your-region-uuid"] }}
🌍 region_ids: Include the region UUID(s) where this file should be available. Use GET /v2.1/region to get your region IDs. If omitted, the file goes into the account's default primary region.
6. Processing -- PXM processes the file asynchronously (OCR, auto-labeling via Google Vision)
7. Available -- File is available via GET /v2.1/file/{id} and can be linked to collections
Confirm Upload for Multipart
{ "file_path": "s3path/temp/upl_abc123/large-video.mp4", "file_name": "large-video.mp4", "file_size": 524288000, "upload_id": "upload-id-from-request-upload", "multipart": [ { "part_number": 1, "etag": "etag-from-s3-response-part-1" }, { "part_number": 2, "etag": "etag-from-s3-response-part-2" } ], "metadata": { "published": true, "region_ids": ["your-region-uuid"] }}
Updating a File
Use PUT /v2.1/file/{id} to update file metadata. Like collections, array fields are fully replaced:
Link a file to collections:
{ "collections": [ {"id": "collection-uuid-1"}, {"id": "collection-uuid-2"} ]}
Update region access on a file:
{ "region_ids": ["region-uuid-1", "region-uuid-2"]}
region_ids: Include region UUIDs to scope access. Omit to use the account default region. This is a full replace -- GET first and merge if needed.
File Response Fields
Field | Type | Description |
| array | Collection IDs this file is linked to -- array of objects with an id property |
| number | File size in bytes |
| string | Image dimensions, e.g., |
| boolean |
|
| array | Region UUIDs this file is scoped to |
| array | Auto-generated labels from Google Vision API |
| string | Text extracted via OCR (Google Vision) |
| object | Links to preview, large, medium, small, and thumb renditions |
Attributes
Attributes are metadata fields that can be attached to collections, files, or categories. Global attributes are defined at the account level and assigned to objects.
Method | Endpoint | Description |
|
| List all global attributes |
|
| Create a global attribute |
|
| Get attribute by ID |
|
| Update a global attribute definition |
|
| Delete a global attribute |
|
| Get attributes on a specific object (entity = collection, file, category) |
|
| Update the value of a specific attribute on an object |
Attribute types supported: Text, Date, List, Pick List, Boolean, Number
To update attributes via collection PUT, include them in the attributes array. Each entry requires an id and a type:
type value | What it does | Notes |
| Set or change the attribute value | Requires a |
| Clear / remove the attribute value | No |
"attributes": [ { "id": "attr-uuid-1", "type": "update", "value": "Red" }, { "id": "attr-uuid-2", "type": "delete" }]
Attribute Groups
Attribute groups organize related attributes into named sections on a collection. They are display-only groupings and do not affect API calls to individual attributes.
Method | Endpoint | Description |
|
| List all attribute groups |
|
| Create an attribute group |
|
| Get an attribute group by ID |
|
| Update an attribute group |
|
| Delete an attribute group |
Search
Search endpoints allow full-text keyword search across folders (collections) and files.
Method | Endpoint | Description |
|
| Search (no total count) |
|
| Search with total count in response |
Entity can be folder or file.
v3 Response Shape:
{ "hits": [{ "id": "...", "name": "..." }], "total": 42}
Regions
Regions define localization scopes in PXM. Files and collections can be scoped to specific regions for multi-market deployments.
Note: In the PXM platform UI, Regions are also referred to as Org Units. The terms are interchangeable -- the API uses region_ids, but you may see "Org Unit" in the platform settings.
Method | Endpoint | Description |
|
| List all regions |
Use GET /v2.1/region to retrieve all region IDs for your account. The primary region has is_primary: true. When you include region_ids on a file or collection, it scopes that content to those specific regions. If you omit region_ids, content defaults to the account's primary/default region.
Field | Type | Description |
| string | Region ID -- use this value in region_ids fields on other objects |
| string | Region display name |
| boolean | Whether this is the primary/default region |
| string | Localization language code |
| array | Collection (product) IDs associated with this region |
Always GET before PUT on array fields -- collections, direct_parent, and region_ids are fully replaced on every PUT. Merge existing values with your updates before writing.
Resources
Resources are URL-based references -- spec sheets, external documents, or links -- that can be attached to categories or collections.
Method | Endpoint | Description |
|
| List all resources |
|
| Create a resource |
|
| Get a resource by ID |
|
| Update a resource |
|
| Delete a resource |
Required: description, link
{ "description": "Product Specification Sheet", "link": "https://example.com/spec-sheet.pdf", "category_id": "category-uuid-here"}
{ "description": "Assembly Instructions", "link": "https://example.com/instructions.pdf", "collections": ["collection-uuid-1", "collection-uuid-2"]}
Field | Type | Required | Description |
| string | Yes | Human-readable label for the resource |
| string | Yes | The URL of the resource |
| string | No | Category this resource belongs to |
| array | No | Collection IDs this resource is linked to |
Pagination
All list endpoints support limit and offset for pagination. The default limit is typically 20. Fetch the next page by incrementing offset by limit. Stop when the returned count is less than limit.
# Page 1GET /v2.1/collection?limit=100&offset=0# Page 2GET /v2.1/collection?limit=100&offset=100# Page 3GET /v2.1/collection?limit=100&offset=200
The same limit/offset approach works for /v2.1/file, /v2.1/category, and other list endpoints.
Understanding PUT Behavior
The PXM API uses PUT for updates -- not PATCH. This has an important implication for array fields: sending a partial array replaces the entire array, not just the items you specify.
Fields affected by full-replacement:
* collections on a file
* direct_parent on a collection
* region_ids on a file or collection
* attributes on a collection (when passed as an array)
Safe update pattern:
# 1. GET the current statecurrent = requests.get(f"{BASE_URL}/v2.1/collection/{id}", headers=headers).json()# 2. Merge your changesexisting_parents = current.get("direct_parent", [])new_parents = existing_parents + ["new-category-uuid"]# 3. PUT the merged valuepayload = {"direct_parent": new_parents}requests.put(f"{BASE_URL}/v2.1/collection/{id}", headers=headers, json=payload)
Production Integration Patterns
Rate Limiting
The PXM API allows 1,000 requests per 60 seconds. When you exceed this limit, you'll receive a 429 Too Many Requests response:
HTTP/1.1 429 Too Many RequestsRetry-After: 60X-RateLimit-Limit: 1000{ "error": "Rate limit exceeded.", "retry_after": 60}
For bulk operations, target 950 requests per 60 seconds to stay safely under the limit.
Retry Logic
import time, requestsdef api_request_with_retry(method, url, **kwargs): for attempt in range(3): resp = requests.request(method, url, **kwargs) if resp.status_code == 429: wait = int(resp.headers.get("Retry-After", 60)) print(f"Rate limited. Waiting {wait}s...") time.sleep(wait) continue resp.raise_for_status() return resp raise Exception("Max retries exceeded")
Delta Sync Pattern
Rather than re-fetching all data on every sync, use date filters to fetch only changed records:
from datetime import datetime, timedeltayesterday = (datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%dT%H:%M:%SZ")# Fetch only collections updated in the last 24 hourspayload = requests.get( f"{BASE_URL}/v2.1/collection", headers=headers, params={ "updated_start_date": yesterday, "limit": 100, "offset": 0, }).json()
Concurrency with Rate Limiting (Python asyncio)
import asyncio, aiohttpclass RateLimiter: def __init__(self, max_calls=950, period=60.0): self._semaphore = asyncio.Semaphore(max_calls) self.period = period self.max_calls = max_calls async def __aenter__(self): await self._semaphore.acquire() async def __aexit__(self, *_): await asyncio.sleep(self.period / self.max_calls) self._semaphore.release()rate_limiter = RateLimiter()async def fetch(session, url): async with rate_limiter: async with session.get(url, headers=headers) as resp: return await resp.json()
Token Caching
PXM tokens are valid for 1 year. Avoid requesting a new token on every script run:
import os, requestsTOKEN_FILE = ".pxm_token"def get_token(): if os.path.exists(TOKEN_FILE): with open(TOKEN_FILE) as f: return f.read().strip() resp = requests.post( "https://entapi.amplifi.io/v2.1/oauth/authorize", auth=(CLIENT_ID, CLIENT_SECRET), json={"grant_type": "client_credentials"}, ) token = resp.json()["access_token"] with open(TOKEN_FILE, "w") as f: f.write(token) return token
Error Codes
Code | Meaning | Common Cause | Retry? |
| Bad Request | Invalid payload or missing required field | No -- fix the request |
| Unauthorized | Missing or expired Bearer token | No -- re-authenticate |
| Forbidden | Token lacks permission for this resource | No -- check credentials |
| Not Found | Resource ID does not exist or was deleted | No -- verify the ID |
| Too Many Requests | Rate limit exceeded (1,000 req/60s) | Yes -- wait Retry-After seconds |
| Server Error | Unexpected error on the PXM side | Yes -- retry with backoff |
| Bad Gateway | Upstream service unavailable | Yes -- retry with backoff |
| Service Unavailable | Temporary outage or maintenance | Yes -- retry with backoff |
Swagger / OpenAPI Notes
The PXM API has an interactive Swagger UI available at https://entapi.amplifi.io/docs/index.html. It is a useful starting point for endpoint discovery and field reference, but there are a few things to be aware of when using it for integration work.
Topic | What to Know |
Field writability | Some fields are marked |
Response wrappers | Some response schemas in Swagger may be simplified or may not fully reflect real response wrappers (e.g., |
API version paths | OAuth/token endpoints and resource endpoints may use different API version paths (e.g., |
Sandbox testing | Always test in a non-production environment before running bulk updates or writes. The Swagger UI can be used to make live API calls against your account. |
Spec currency | The Swagger spec may not always reflect the latest endpoint additions or behavior changes. When in doubt, validate against the live API and refer to this article or reach out to your CSM. |
This article reflects practical, tested behavior. Where Swagger and this documentation differ, follow this article.
Best Practices
* Always GET before PUT on array fields -- collections, direct_parent, and region_ids are fully replaced on every PUT. Merge existing values with your updates before writing.
* Handle response shapes defensively -- Use the defensive parsing pattern in the Response Format Variations section. Do not assume all list endpoints return the same JSON structure.
* Validate payloads before sending -- Several issues in integrations come from incorrect data values, not platform bugs. Test against the API schema before scaling up.
* Respect rate limits -- Target 950 requests/60s to leave a buffer. Implement retry logic with exponential backoff for 429s.
* Cache your token -- Tokens last 1 year. There is no need to request a new one on every run.
* Use delta syncs -- Use updated_start_date / updated_end_date filters on list endpoints to fetch only changed records rather than pulling everything every time.
* Test in a sandbox first -- Run bulk write operations on a small batch before scaling to your full catalog. Confirm the behavior matches expectations before processing thousands of records.
Need Help?
If you run into issues with the API or need help with a specific integration, reach out to your Customer Success Manager or contact PXM Support through the in-app chat.
