Small demo that:
- pulls products from Google Sheets,
- resolves
productTypevia SP-API Definitions (or uses overrides), - builds Listings Items payloads,
- optionally submits them (
--submit).
In the US sandbox the Definitions search often returns only one productType: LUGGAGE. This is a SP-API sandbox constraint, not a bug in the code. To get real product types you need a production Seller Central account (paid), verification, and production credentials.
Amazon confirms static sandbox data: Product Type Definitions in sandbox provide only LUGGAGE as test product type; see Amazon maintainer note: “Please note that the sandbox in most cases returns limited static data, if you need production data it is not recommended to use the sandbox environment.” (ref: amzn/selling-partner-api-models#3837)
Workarounds:
- Add a
product_typecolumn in the sheet to override per row. - Set
SPAPI_DEFAULT_PRODUCT_TYPE/SPAPI_PRODUCT_TYPE_PREFERRED_TOKENSinconfig/.envto bias selection or provide a fallback. - Use production credentials on an account with access to the needed categories.
- Python 3.11+, httpx, pydantic, google-api-python-client, dotenv.
- Tooling: ruff, black, flake8.
src/sheets/— Google Sheets reader.src/spapi/— LWA auth, SP-API client, product type search.src/validators/— input models, payload builder, productType resolver.src/utils/— config loader, logging.config/.env.example— env template.logs/— rotating logs.
- Copy
config/.env.example→config/.envand fill:- Google:
GOOGLE_SHEETS_ID,GOOGLE_SHEETS_RANGE(e.g.INVENTORY!A1:T),GOOGLE_CREDENTIALS_FILE(service account JSON with sheet access) orGOOGLE_API_KEY. - SP-API (sandbox creds from task work for LWA):
SPAPI_CLIENT_ID,SPAPI_CLIENT_SECRET,SPAPI_REFRESH_TOKENSPAPI_ENDPOINT=https://sandbox.sellingpartnerapi-na.amazon.comSPAPI_MARKETPLACE_ID=ATVPDKIKX0DERSPAPI_SELLER_ID=<seller_id>
- Optional resolver tuning:
SPAPI_DEFAULT_PRODUCT_TYPE— fallback on empty search.SPAPI_PRODUCT_TYPE_PREFERRED_TOKENS— bias selection (comma-separated).
- Brand fallback:
SPAPI_DEFAULT_BRAND(used if sheet has no brand column).
- Google:
- Install deps:
pip install -r requirements.txt.
- Dry-run (no submission, logs payloads):
python -m src.main --log-level DEBUG - Submit Listings Items:
python -m src.main --submit --log-level INFO - Override sheet/range/marketplace on the fly:
python -m src.main --sheet-id <id> --range INVENTORY!A1:T --marketplace ATVPDKIKX0DER
- If the sheet has
product_type, it is used directly. - Else: keywords from title/model/mpn/color → Definitions search → choose best candidate (or first if no preferences).
- If no candidates and no
SPAPI_DEFAULT_PRODUCT_TYPE, raises an error. In sandbox this is expected when onlyLUGGAGEis available.
- Uses brand from sheet if present, otherwise
SPAPI_DEFAULT_BRAND. - Maps basic fields (title, description, bullets, UPC, price, availability, images); extend as needed for specific productType schemas.
- Console +
logs/app.log(10 MB rotation). INFO shows keywords, productType candidates, final choice, and payload summary.
python -m src.main --log-level DEBUG
... Starting run for sheet=... submit=False
... Parsed 7 products from sheet
... Resolved keywords for sku=Kushtym1: [...]
... Candidates for keywords=[...]: ['LUGGAGE']
... Dry-run payload for sku=Kushtym1 productType=LUGGAGE (reason=search_match): {...}
python -m src.main --submit --log-level INFO
... Starting run for sheet=... submit=True
... Parsed 7 products from sheet
... Candidates for keywords=[...]: ['LUGGAGE']
... Submitted listing sku=Kushtym1 productType=LUGGAGE response={'sku': 'GM-ZDPI-9B4E', 'status': 'ACCEPTED', ...}
- Minimal unit tests for parser, resolver, and payload builder:
pytest -q
- Use production creds and real categories to get correct productTypes.
- Map attributes against Product Type Definitions schemas and validate.
- Add Feeds API submission and polling.
- Expand tests: parser, resolver, builder, SP-API stubs.
MIT © 2025 ursaloper