# Pulling Orders Data This tutorial covers the four ways to get order data out of OmniCart, from paginated JSON pulls (best for scheduled syncs) to bulk CSV exports (best for historical backfills). Prerequisite: an admin token — see [Data Access Overview](/tutorials/overview). --- ## 1. Paginated orders pull — `GET /admin/orders` The workhorse for scheduled syncs into a warehouse or BI pipeline. ```bash curl -G 'https://your-store.omnicart.cc/admin/orders' \ -H 'Authorization: Bearer YOUR_TOKEN' \ --data-urlencode 'limit=100' \ --data-urlencode 'offset=0' \ --data-urlencode 'order=created_at' \ --data-urlencode 'created_at[$gte]=2026-07-01T00:00:00Z' \ --data-urlencode 'created_at[$lt]=2026-07-02T00:00:00Z' \ --data-urlencode 'fields=id,display_id,status,email,total,currency_code,created_at,updated_at,sales_channel_id' ``` **Key query parameters:** | Param | Notes | |-------|-------| | `limit` / `offset` | Pagination. Default limit is small (15–20) — set it explicitly. | | `order` | Sort field; prefix `-` for descending (e.g. `-created_at`). Sort ascending by `created_at` for stable pagination during syncs. | | `created_at` / `updated_at` | Operator maps: `[$gte]`, `[$lte]`, `[$lt]`, `[$gt]`. Values are exact instants — send ISO timestamps with explicit `Z`/offset. | | `status` | Order status, single value or array. | | `sales_channel_id` | Filter to one or more stores/channels. | | `customer_id`, `region_id`, `q` | Additional filters / free-text search. | | `fields` | Comma-separated field list. Use `*items` to expand line items, `*shipping_address` for addresses, `fulfillments.*`, `payment_collections.*` for related records. | **Response shape:** ```json { "orders": [ { "id": "order_01...", "display_id": 12345, "status": "completed", "total": 89.97, "created_at": "2026-07-01T14:22:31.000Z", "...": "..." } ], "count": 1284, "limit": 100, "offset": 0 } ``` `count` is the total match count — loop `offset += limit` until you've fetched `count` rows. **Incremental sync recipe:** store the max `updated_at` you've seen, then each run filter `updated_at[$gte]=` and upsert by `id`. Using `updated_at` (not `created_at`) means status changes, refunds, and fulfillment updates flow into your warehouse too. Remember: `total` and all money fields are **decimal dollars** (`89.97` = $89.97). --- ## 2. Fulfillment & shipping view — `GET /admin/fulfillment` A slim, fast list oriented around shipping operations: shipped state, carrier lifecycle, notification-email status. Ideal for ops reporting (e.g. "orders placed > 3 days ago, not yet shipped"). ```bash curl -G 'https://your-store.omnicart.cc/admin/fulfillment' \ -H 'Authorization: Bearer YOUR_TOKEN' \ --data-urlencode 'limit=100' \ --data-urlencode 'fulfillment_filter=awaiting' \ --data-urlencode 'created_at_gte=2026-07-01T00:00:00Z' \ --data-urlencode 'no_cache=1' ``` **Useful parameters:** | Param | Notes | |-------|-------| | `fulfillment_filter` | `awaiting`, `shipped`, `shipped_no_email`, `email_sent`, `email_missed`, `on_hold`, `needs_review`, `quickbox_errors`, `cancelled`, `all` | | `created_at_gte` / `created_at_lte` | Date window (exact instants) | | `date_axis` | Which date the window applies to: `placed`, `shipped`, `email_sent`, or `auto` | | `placed_at_*`, `shipped_at_*`, `email_sent_*` | Per-field date ranges (`_gte`/`_lte`) that combine freely | | `has_shipped`, `email_sent`, `on_hold`, `needs_review` | Tri-state boolean filters (`true`/`false`, omit for "any") | | `sales_channel_id`, `q`, `order` | Channel filter, search (order # or email), sort | | `no_cache=1` | Bypass the server cache (responses are otherwise up to 3 minutes stale; check the `X-Cache` header) | **Always bound fulfillment queries with a date window** (`created_at_gte`/`created_at_lte` or the per-field ranges). On stores with large order histories, unbounded filtered queries can exceed internal query timeouts and fail — a date window keeps them fast and reliable. Each row includes `display_id`, `email`, `total`, `status`, `payment_status`, `is_shipped`, `shipped_at`, shipping-email timestamps, carrier lifecycle fields, and `sales_channel` — but **not** line items or addresses. Fetch a single order's full detail with `GET /admin/fulfillment/:id`. On very large unfiltered queries, `count` may be an estimate. --- ## 3. CSV exports ### Fulfillment table CSV — `GET /admin/fulfillment/export-csv` Same filters as the list above; returns a CSV (with header row) matching the dashboard table: order #, dates, recipient, email, store, sync status, carrier lifecycle timestamps, total, currency. ```bash curl -G 'https://your-store.omnicart.cc/admin/fulfillment/export-csv' \ -H 'Authorization: Bearer YOUR_TOKEN' \ --data-urlencode 'fulfillment_filter=shipped' \ --data-urlencode 'created_at_gte=2026-06-01T00:00:00Z' \ -o shipped-orders.csv ``` Capped at **50,000 rows**. If your filter matches more, the response carries an `X-Export-Truncated: true` header — narrow the date window and export in chunks. ### Bulk order export (async) — `POST /admin/orders/export` The intended path for large historical exports. Returns immediately with a transaction id; the CSV is generated in the background and delivered as a notification in the admin dashboard feed with a download URL. ```bash curl -X POST 'https://your-store.omnicart.cc/admin/orders/export?created_at[$gte]=2026-01-01&created_at[$lte]=2026-06-30' \ -H 'Authorization: Bearer YOUR_TOKEN' ``` Response: `202 Accepted` with `{ "transaction_id": "..." }`. Two things make this endpoint different: 1. **Date filters snap to calendar days in the report timezone (US Eastern by default).** `created_at[$gte]=2026-01-01` means midnight Eastern on Jan 1, and the upper bound is extended to the *end* of its calendar day. This is what you want for "monthly report" semantics; it is different from the raw-instant filtering everywhere else. You can pass a `timezone` parameter (IANA name, e.g. `America/Chicago`) to change it per-request. 2. **The CSV is not in the HTTP response.** Watch the admin notification feed for the file link. Columns include order IDs, status, date (rendered in the report timezone), customer name/email/ID, shipping address, fulfillment & payment status, subtotal/shipping/discount/tax/total, currency, and a compact items summary. ### Tax report — `GET /admin/reports/taxes/export` ```bash curl -G 'https://your-store.omnicart.cc/admin/reports/taxes/export' \ -H 'Authorization: Bearer YOUR_TOKEN' \ --data-urlencode 'start_date=2026-04-01T00:00:00Z' \ --data-urlencode 'end_date=2026-06-30T23:59:59Z' \ --data-urlencode 'format=json' \ --data-urlencode 'include_orders=true' ``` `start_date` and `end_date` are required. `format` is `csv` (default) or `json`. JSON returns `summaryByState` (order count, taxable amount, tax collected, average rate per state) plus `totalTaxCollected`, and per-line `orderDetails` when `include_orders=true`. Add `state=TX` to scope to one state. Keep windows reasonable (a quarter at a time) — this endpoint scans the full range in one pass. --- ## Gotchas checklist for scheduled jobs - Money is **decimal dollars** everywhere. Never divide by 100. - Send **explicit ISO timestamps** on all date filters except when you deliberately want the async export's Eastern-calendar-day behavior. - Prefer a secret API key (`-u "sk_...:"`) for scheduled jobs; if using JWTs, re-authenticate on any 401 (tokens last 7 days by default, but don't hard-code that). - Use `updated_at` cursors + upsert-by-`id` for incremental syncs. - Check `X-Export-Truncated` on CSV exports and `X-Cache` on fulfillment reads.