--- title: Feed management API description: Register feeds, attach mappings, trigger a synchronization and read run history over the catalog configuration API. slug: indexing/feed-management-api docKind: guide hub: luigisbox-ai --- Feeds are normally configured with your Luigi's Box implementation contact. When you want to manage them yourself — a new market, a URL rotation, a scripted resynchronization — the catalog configuration API does the same job. Everything here needs a token for the `https://api..luigisbox.ai/catalog` audience, and a grant covering the operation — feeds, mappings, syncs and metadata are granted separately, and reading is separate from writing. See [Server-to-server tokens](/authentication/server-to-server/). ## The pieces | Resource | What it is | |---|---| | **Feed** | One URL, one object type, its format and its parsing options | | **Mapping** | How source fields become catalog fields — see [Field mapping](/indexing/mapping/). Shared with the [Content API](/indexing/content-api/). | | **Synchronization** | One run: download, parse, send. Recorded whether it succeeds or not. | One catalog holds several feeds — products, categories, brands, articles — each pointing at a mapping. ## Registering a feed ```bash curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com/feeds/products.xml.gz", "catalog_type": "product", "format": "xml", "item_tag": "product", "identity_field": "product_id", "mapping_id": 17, "active": true }' ``` The response is the created feed, including its `feed_id` and `version`. | Field | Required | Purpose | |---|---|---| | `url` | Yes | Where to fetch the feed. HTTPS, no interactive login. | | `catalog_type` | Yes | The object type in this file: `product`, `category`, `brand`, `article` | | `format` | No | `xml`, `json`, `json_lines`, `csv`. Detected when omitted. | | `item_tag` | No | XML element that wraps one record, e.g. `product` | | `json_path` | No | Path to the record array in a JSON feed, e.g. `products` | | `csv_delimiter` / `csv_has_header` | No | CSV parsing options | | `identity_field` | No | Source field holding the record's identity | | `mapping_id` | No | The mapping to apply | | `group_id_field` / `group_primary_field` | No | Source fields for [variant grouping](/concepts/catalog-object-model/#variant-groups) | | `delete_threshold` | No | Fraction of the catalog a run may remove, `0.0`–`1.0` | | `active` | No | Defaults to `true`. `false` registers without scheduling. | | `position` | No | Ordering among the catalog's feeds | | `transport_options` | No | Extra options for fetching, such as authentication headers | Format, item tag and JSON path are detected from the file when you leave them out. Set them explicitly for a production feed: detection is a convenience, not a contract. :::caution[`delete_threshold` refuses runs that remove too much] A run that would remove more than the threshold's share of the catalog is refused, because that is what a truncated or half-built export looks like. Do not raise it to push through a failing run — confirm the export is complete first. For a genuinely large planned reduction, coordinate with Luigi's Box support. ::: ## Reading and changing a feed ```bash # One feed curl 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41' \ -H 'Authorization: Bearer ' # All of a catalog's feeds curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds' \ --data-urlencode 'page=1' --data-urlencode 'size=20' \ -H 'Authorization: Bearer ' ``` Updates are `PATCH`, and they carry the `version` you last read as `expected_version`: ```bash curl -X PATCH 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://example.com/feeds/products-v2.xml.gz", "expected_version": 7 }' ``` If the feed changed since you read it, the version no longer matches and the update is rejected instead of overwriting the other change. Re-read the feed and reapply your change. Omitted fields are left unchanged; a field sent as `null` is cleared. See [Partial updates](/api-basics/requests-and-responses/#partial-updates). `DELETE` deactivates a feed. It stops being fetched; the catalog content it produced stays where it is. To remove the content too, delete the objects — see [Content API](/indexing/content-api/#delete). ## Triggering a synchronization Feeds are checked automatically. Trigger one by hand when you have just changed a mapping, or want to pull a fresh export immediately: ```bash curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sync' \ -H 'Authorization: Bearer ' ``` The response identifies the run: ```json { "run_id": "9f2c4b71-3e8a-4d05-b6c1-7a3d9e2f8041" } ``` This covers the whole catalog — every active feed on it. ### Forcing a full resend Between runs, Luigi's Box remembers which records it has already seen, so an unchanged record is not re-sent. A mapping change therefore does not reshape records that did not change. Reset the memory to force everything through again: ```bash curl -X POST 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/fingerprints/reset' \ -H 'Authorization: Bearer ' ``` Then synchronize. The next run re-sends every record in the feed. Do this after changing a mapping, after adding a field you want backfilled, or when a catalog and a feed have drifted apart. Do not do it routinely — it is a full reindex of that feed's content. The `fingerprints=false` query parameter on `/sync` has the same effect for one run without clearing the stored state. ## Checking status ```bash curl 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/status' \ -H 'Authorization: Bearer ' ``` The latest run, plus what was detected and remembered about the feed: ```json { "feed_id": 41, "locked": false, "latest_run": { "id": 5182, "status": "succeeded", "trigger": "schedule", "started_at": "2026-03-14T02:00:04Z", "finished_at": "2026-03-14T02:04:31Z", "items_sent": 207993, "items_skipped": 0, "items_no_identity": 0, "error_message": null }, "state": { "detected_format": "xml", "detected_item_tag": "product", "total_items": 207993, "last_run_at": "2026-03-14T02:00:04Z" } } ``` `locked: true` means a run is in progress. `state` reports what was detected and remembered about the feed; use it to confirm that format detection agrees with your configuration. ### Run history ```bash curl -G 'https://api.eu1.luigisbox.ai/catalog/v1/lbc_8w3k2p/sources/feeds/41/syncs' \ --data-urlencode 'size=20' \ -H 'Authorization: Bearer ' ``` | `status` | Meaning | |---|---| | `running` | In progress | | `succeeded` | Completed | | `failed` | Stopped — read `error_message` and `status_reason` | | `skipped` | Nothing to do; the feed had not changed since the last run | A long stretch of `skipped` runs means your `ETag` or `Last-Modified` headers work and the unchanged file is not downloaded. A long stretch of `skipped` when the file *has* changed means those headers are wrong — see [Serving your feed](/indexing/feeds/#serving-your-feed). The counters on each run: | Counter | Watch for | |---|---| | `items_sent` | A sudden drop — an incomplete export | | `items_skipped` | Anything non-zero — records rejected during parsing | | `items_no_identity` | Anything non-zero — records with no value in `identity_field` | ## Managing mappings Mappings live under the same catalog: ```text GET /catalog/v1/{catalog_id}/mappings POST /catalog/v1/{catalog_id}/mappings POST /catalog/v1/{catalog_id}/mappings/validate GET /catalog/v1/{catalog_id}/mappings/{mapping_id} PATCH /catalog/v1/{catalog_id}/mappings/{mapping_id} DELETE /catalog/v1/{catalog_id}/mappings/{mapping_id} ``` Always dry-run a change through `/validate` before saving it. See [Field mapping](/indexing/mapping/#try-it-before-you-save). ## See also - [Feeds overview](/indexing/feeds/) — hosting requirements and formats - [Field mapping](/indexing/mapping/) · [Catalog metadata](/indexing/catalog-metadata/) - [Object history](/indexing/object-history/) — tracing one object through a run - [Feed troubleshooting](/indexing/feeds/troubleshooting/)