Skip to main content

How to Connect Amazon Marketplace to Odoo (SP-API, 2026)

Sync Amazon Marketplace orders, FBA inventory, and product listings to Odoo 19 via SP-API. OAuth, webhooks, multi-marketplace, FBA reconciliation.

E
ECOSIRE Research and Development Team
|May 4, 20269 min read1.7k Words|

By the end of this recipe, your Amazon seller account will sync orders, inventory, and product listings to Odoo 19 via the new Selling Partner API (SP-API), with FBA inventory reconciled nightly, MFN orders generating immediate Odoo SOs, and Amazon-charged fees posted as the right vendor bills against your accounting…

By the end of this recipe, your Amazon seller account will sync orders, inventory, and product listings to Odoo 19 via the new Selling Partner API (SP-API), with FBA inventory reconciled nightly, MFN orders generating immediate Odoo SOs, and Amazon-charged fees posted as the right vendor bills against your accounting books. Skill required: senior Odoo developer with REST API + AWS Signature V4 experience. Time required: 8 hours setup, 4 hours testing across MFN and FBA flows. ECOSIRE has built this for Amazon sellers grossing $5M to $50M/year, and the recipe below is the playbook we ship.

The core complication: SP-API replaced MWS (Marketplace Web Service) in 2024, and the migration broke every Odoo connector that targeted MWS. SP-API uses OAuth 2.0 + AWS Sigv4 signing for every request — significantly more setup than MWS but more reliable once configured. The recipe below targets SP-API only.

What you will need

  • Amazon Seller Central account with Professional seller plan ($39.99/month US).
  • SP-API Developer registration at developer.amazon.com (free).
  • AWS account for the IAM user that signs SP-API requests.
  • LWA (Login with Amazon) app: created in Seller Central > Develop Apps.
  • Odoo version: 17, 18, or 19. The OCA connector_amazon module needs forking for SP-API; we typically build a thin custom connector instead.
  • Time: 8 hours setup, 4 hours testing.
  • Skill: REST APIs, AWS Sigv4 signing, OAuth 2.0 token refresh.

Step-by-step

1. Register as an SP-API developer

In Seller Central > Apps and Services > Develop Apps > Add new app client. Set "API Type" = "SP-API". Pick the data scopes you need: Orders, Inventory, Listings, Reports. Save the LWA Client ID and LWA Client Secret.

In a separate tab, create an IAM user in AWS with policy AWSSellingPartnerAPIRole attached. Save the access key ID + secret access key. Verification: app status shows "Active" in Seller Central.

2. Authorize the app for your seller

Go to the app's "Authorize" tab. Click the magic link, log in to your Amazon seller account, and approve. You receive a refresh_token valid for ~1 year. Paste it into Odoo config:

class AmazonBackend(models.Model):
    _name = 'amazon.backend'

    name = fields.Char(required=True)
    lwa_client_id = fields.Char()
    lwa_client_secret = fields.Char()
    refresh_token = fields.Char()
    aws_access_key_id = fields.Char()
    aws_secret_access_key = fields.Char()
    region = fields.Selection([('us-east-1', 'NA'), ('eu-west-1', 'EU'), ('us-west-2', 'FE')])
    marketplace_ids = fields.Char(default='ATVPDKIKX0DER')  # ATVPDKIKX0DER = US

Verification: the backend record saves and you can manually trigger token refresh.

3. Implement OAuth token exchange

SP-API requires an access_token (1-hour lifetime) for every request. Refresh from the long-lived refresh_token:

import requests

def _get_access_token(self):
    cache_key = f'amazon.access_token.{self.id}'
    cached = self.env['ir.config_parameter'].sudo().get_param(cache_key)
    if cached:
        token, expiry = cached.split('|')
        if datetime.now().timestamp() < float(expiry) - 60:
            return token
    resp = requests.post('https://api.amazon.com/auth/o2/token', data={
        'grant_type': 'refresh_token',
        'refresh_token': self.refresh_token,
        'client_id': self.lwa_client_id,
        'client_secret': self.lwa_client_secret,
    }, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    expiry = datetime.now().timestamp() + data['expires_in']
    self.env['ir.config_parameter'].sudo().set_param(
        cache_key, f"{data['access_token']}|{expiry}"
    )
    return data['access_token']

Verification: the function returns a non-empty token; calling it 5 times in a row returns the same cached token until expiry.

4. Sign requests with AWS Sigv4

SP-API requires AWS Sigv4 signing in addition to the LWA token. Use the official requests-aws4auth library:

from requests_aws4auth import AWS4Auth

def _signed_request(self, method, path, params=None, json=None):
    token = self._get_access_token()
    auth = AWS4Auth(
        self.aws_access_key_id,
        self.aws_secret_access_key,
        self.region,
        'execute-api',
    )
    base_url = {
        'us-east-1': 'https://sellingpartnerapi-na.amazon.com',
        'eu-west-1': 'https://sellingpartnerapi-eu.amazon.com',
        'us-west-2': 'https://sellingpartnerapi-fe.amazon.com',
    }[self.region]
    headers = {'x-amz-access-token': token}
    return requests.request(method, base_url + path,
                            params=params, json=json,
                            headers=headers, auth=auth, timeout=60)

Verification: a GET /sellers/v1/marketplaceParticipations returns a 200 with the list of marketplaces you sell on.

5. Pull orders

SP-API's Orders endpoint paginates by NextToken. Pull last 24 hours every 15 minutes:

def _pull_recent_orders(self):
    cutoff = (datetime.utcnow() - timedelta(hours=24)).isoformat() + 'Z'
    next_token = None
    while True:
        params = {
            'MarketplaceIds': self.marketplace_ids,
            'CreatedAfter': cutoff,
        }
        if next_token:
            params['NextToken'] = next_token
        resp = self._signed_request('GET', '/orders/v0/orders', params=params)
        resp.raise_for_status()
        data = resp.json()['payload']
        for order in data.get('Orders', []):
            self._upsert_amazon_order(order)
        next_token = data.get('NextToken')
        if not next_token:
            break

For each order, fetch line items separately via GET /orders/v0/orders/{orderId}/orderItems, then create the Odoo SO:

def _upsert_amazon_order(self, order_data):
    SO = self.env['sale.order']
    existing = SO.search([('amazon_order_id', '=', order_data['AmazonOrderId'])])
    if existing:
        return existing
    partner = self._get_or_create_amazon_partner(order_data)
    items_resp = self._signed_request('GET', f"/orders/v0/orders/{order_data['AmazonOrderId']}/orderItems")
    line_data = items_resp.json()['payload'].get('OrderItems', [])
    so = SO.create({
        'partner_id': partner.id,
        'amazon_order_id': order_data['AmazonOrderId'],
        'amazon_marketplace': order_data['MarketplaceId'],
        'date_order': order_data['PurchaseDate'],
        'order_line': [(0, 0, {
            'product_id': self._get_product_by_sku(item['SellerSKU']).id,
            'product_uom_qty': item['QuantityOrdered'],
            'price_unit': float(item['ItemPrice']['Amount']),
        }) for item in line_data],
    })
    so.action_confirm()
    return so

Verification: place a real Amazon order on a test SKU, run the pull, and the SO appears in Odoo within 15 minutes.

6. Reconcile FBA inventory

Run a daily cron to pull FBA inventory levels and update product.product.qty_available on the FBA stock location:

def _cron_pull_fba_inventory(self):
    resp = self._signed_request('GET', '/fba/inventory/v1/summaries', params={
        'granularityType': 'Marketplace',
        'granularityId': self.marketplace_ids,
        'marketplaceIds': self.marketplace_ids,
    })
    fba_loc = self.env.ref('stock.stock_location_amazon_fba')
    for summary in resp.json()['payload']['inventorySummaries']:
        product = self.env['product.product'].search([('default_code', '=', summary['sellerSku'])], limit=1)
        if not product:
            continue
        qty = summary['inventoryDetails']['fulfillableQuantity']
        # Adjust the on-hand quantity for the FBA location
        self.env['stock.quant']._update_available_quantity(product, fba_loc, qty)

Verification: FBA-warehouse on-hand in Odoo matches the Seller Central FBA dashboard within 1 day.

7. Pull settlement reports for accounting

Amazon settles every 2 weeks with a settlement report. Pull it via POST /reports/2021-06-30/reports with reportType=GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2. Parse the CSV and post journal entries:

import csv

def _process_settlement_report(self, report_id):
    # 1. Request the report
    resp = self._signed_request('POST', '/reports/2021-06-30/reports', json={
        'reportType': 'GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2',
        'marketplaceIds': [self.marketplace_ids],
    })
    # 2. Poll until DONE; fetch the document URL; download
    # 3. Parse rows and create the journal entry
    move_lines = []
    with open('/tmp/settlement.csv') as f:
        for row in csv.DictReader(f, delimiter='\t'):
            account = self._map_amazon_fee_to_account(row['transaction-type'])
            move_lines.append((0, 0, {
                'account_id': account.id,
                'debit': max(0, -float(row['amount'])),
                'credit': max(0, float(row['amount'])),
                'name': row['description'],
            }))
    self.env['account.move'].create({
        'move_type': 'entry',
        'journal_id': self.env.ref('account.journal_amazon_settlement').id,
        'line_ids': move_lines,
    }).action_post()

Verification: the journal entry totals match the bank deposit amount Amazon paid you.

8. Push tracking back for MFN orders

For Merchant Fulfilled Network orders, after the Odoo Delivery is validated with a tracking number, push to Amazon:

def _push_fulfillment(self, picking):
    self._signed_request('POST', '/orders/v0/orders/{order_id}/shipment', json={
        'shipmentDate': fields.Datetime.now().isoformat(),
        'carrierName': picking.carrier_id.name,
        'trackingNumber': picking.carrier_tracking_ref,
        'shipFromAddress': self._company_address(),
    })

Verification: Amazon order page shows "Shipped" status with the tracking number within minutes.

Common mistakes

  • Skipping AWS Sigv4 signing. SP-API rejects unsigned requests with a confusing 403.
  • Hardcoding the marketplace endpoint. NA, EU, and FE have different base URLs.
  • Polling orders more than every 60 seconds. SP-API rate limits aggressively (5 burst, 0.0167/sec sustained for orders).
  • Not idempotent on order creation. A network blip causes duplicate orders. Always check amazon_order_id first.
  • Missing fee mapping. Amazon has 50+ fee types (FBA fee, referral fee, storage fee). Map each to the right Odoo expense account.

Going further

Multi-region sellers: register one app per region (NA + EU + FE), each with its own refresh token. The Odoo backend per region pulls in parallel. North American sellers expanding to UK/EU multiply their addressable market roughly 3x but the listings, taxes, and returns logistics per region need different tuning.

Listing automation: push new Odoo products to Amazon as listings via /listings/2021-08-01/items. Includes images, bullet points, search terms, and category-specific attributes. Each Amazon category has its own attribute schema — pull the schema dynamically and map your Odoo fields.

Buy Box monitoring: pull pricing for your ASINs every hour and alert if you lose the Buy Box. Drives competitive repricing — most ECOSIRE clients run a small ML repricer that adjusts prices within a band to maintain Buy Box ownership while protecting margin.

Returns and refunds: subscribe to the Returns API and create return pickings + credit notes automatically. Returns are a major P&L drag for high-volume sellers; automate the processing to reduce labor cost.

Sponsored Products / Brands ads attribution: pull ad spend from Amazon Advertising API and attribute to the orders. Compute per-keyword ROAS visible in Odoo CRM.

Inventory aging in FBA: FBA storage fees climb with age. Pull the FBA inventory aging report and trigger clearance pricing for SKUs aged 180+ days.

Account health monitoring: pull the Account Health report daily. Flag any negative metrics (late shipments, customer complaints) for immediate intervention.

Vine reviews tracking: for new products in Vine, track review velocity and sentiment. Pair with sales data to see when 5-7 reviews tip the conversion rate.

Brand registry integration: if you own a registered brand on Amazon, pull the Brand Analytics reports for share-of-search, top search terms, and market basket analysis.

Q4 surge planning: build a forecasting model that scales daily inventory targets through October-December based on historical seasonality.

Cross-marketplace inventory pooling: if you sell on multi-marketplaces (Amazon US + Amazon CA + Walmart + your own DTC), build a unified inventory layer that allocates across channels based on velocity and margin.

Settlement-to-bank reconciliation: pair the settlement journal with bank statement parsing so cash receipts auto-clear settlement liability accounts. End-to-end automation saves the AR clerk hours per month.

For full Amazon + Odoo integration including multi-region, FBA reconciliation, and Buy Box automation, ECOSIRE custom Odoo development builds the complete stack. Pair this with how to configure Odoo warehouse routes for the FBA picking flow.

Frequently Asked Questions

What does SP-API cost?

The API itself is free for sellers, but a Professional Seller plan ($39.99/month US) is required. For very high volume there are throttling limits that can be raised by request — Amazon evaluates case by case.

Can I sync historical orders?

Yes, but Amazon's Orders API only goes back 2 years. For older history, pull settlement reports going back further (those are available longer).

How do I handle multiple seller accounts in one Odoo?

One backend record per Amazon account. Tag each SO with amazon_account_id so reporting can split by account.

What about VAT/sales tax?

Amazon withholds VAT in the EU/UK and remits it directly. The settlement report shows VAT-collected lines that should be posted to a "VAT collected by Amazon" liability account, then cleared when Amazon remits.

For the full Amazon + Odoo + accounting integration including settlement reconciliation and tax handling, our Odoo integration team ships fixed-price engagements. Or read how to integrate Shopify with Odoo if Amazon is one of multiple channels.

E

Written by

ECOSIRE Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

Transform Your Business with Odoo ERP

Expert Odoo implementation, customization, and support to streamline your operations.