Epicor REST API v2: OData Filter, Select and Expand Examples
Epicor REST API v2 OData filter example
Also searched as
- Epicor REST v2 $filter $select $top
- Epicor API call to get jobs odata
- Epicor REST API x-api-key header example
- Epicor BAQ via REST v2 odata
Short answer
Epicor Kinetic REST v2 exposes business objects at /api/v2/odata/{Company}/{Service}/{Entity} and BAQs at /api/v2/odata/{Company}/BaqSvc/{BAQId}/Data. Send an API key in the x-api-key header plus credentials, and use standard OData options: $filter, $select, $top, $skip, $orderby and $expand.
Applies to: Epicor Kinetic 2021.x to current with REST v2 enabled; v1 uses a different URL pattern and is not covered here
Make your first useful call
- 1Generate an API key in Epicor: API Key Maintenance, for the user or integration account, and note the key value.
- 2Build the base URL: https://yourserver/yourinstance/api/v2/odata/{Company}/ followed by the service and entity.
- 3Send two headers: x-api-key with the key, and Authorization with Basic credentials or a bearer token, depending on your authentication setup.
- 4Request a small result first: add $top=5 to confirm connectivity and see the property names exactly as spelled.
- 5Add $select to return only the columns you need; it reduces payload and avoids exposing unneeded data.
- 6Add $filter to narrow rows. Use eq, ne, gt, lt, and, or, and string functions such as startswith and contains.
- 7Page with $top and $skip, or follow the @odata.nextLink value in the response, until no more rows return.
- 8Call a BAQ by name from BaqSvc, and pass filter values as OData options on the BAQ result fields.
URL patterns and examples
The service name matches the business object service, such as Erp.BO.JobEntrySvc, Erp.BO.PartSvc or Erp.BO.SalesOrderSvc, and the entity is the table-like collection exposed by it, such as Jobs, Parts or OrderHeds. The easiest way to learn the exact names is the REST help page on your own server, which lists services and fields for your version.
String values in a filter need single quotes. Dates use the OData format, either with a datetime literal or a date function. Spaces in the URL must be encoded as %20 in a raw HTTP client, though most libraries do this for you.
GET /api/v2/odata/EPIC06/Erp.BO.JobEntrySvc/JobHeads?$select=JobNum,PartNum,JobReleased&$filter=JobReleased eq true and JobClosed eq false&$top=50&$orderby=JobNum desc
GET /api/v2/odata/EPIC06/Erp.BO.PartSvc/Parts?$filter=startswith(PartNum,'AX-') and InActive eq false&$select=PartNum,PartDescription,TypeCode
GET /api/v2/odata/EPIC06/BaqSvc/MyOpenJobs/Data?$filter=JobHead_JobNum eq 'J-100234'
Headers, authentication and what 401 and 403 mean
REST v2 normally needs the API key and a valid user. A missing key returns an error that names the key, while bad credentials give a 401. A 403 usually means the user authenticated but has no security for that service or company, so check the user's security group and the company in the URL.
Do not put credentials in URLs and do not log request headers. Keep keys in a secrets store and give the integration its own Epicor user with the minimum rights, so a leaked key has limited reach. For the 401 case in detail, see the dedicated page on that error.
curl -s "https://erp.example.com/prod/api/v2/odata/EPIC06/Erp.BO.PartSvc/Parts?$top=5&$select=PartNum" -H "x-api-key: YOUR_KEY" -H "Authorization: Basic BASE64_USER_PASS" -H "Accept: application/json"
Expand, paging and write calls
$expand pulls child collections in one call, for example a job's assemblies and materials, but each expansion multiplies payload size. Use it for a single record or a small page; for bulk, make separate calls per entity and join in your own code.
Results are capped by server settings. If you ask for more than the page size, you get a nextLink to follow. Always loop until it is absent rather than assuming one response is complete. For writes, use the business object's methods, such as the GetNew and Update methods, so BPM and validation run. Posting straight to a table entity is rarely what you want.
BAQ endpoints and performance
A BAQ exposed through BaqSvc is often the best integration source. It lets an Epicor developer write the joins and filters once, tested and secured, and the integration reads a stable, flat result. Column names come back in the form Table_Column.
Keep $filter on indexed or key fields where possible. A filter on a computed BAQ field cannot use an index and may scan. For large extracts, schedule them off-hours and page them.
Common pitfalls
- !Forgetting the x-api-key header and treating the resulting error as a bad password.
- !Writing field names from memory; property names are case sensitive and version specific.
- !Requesting a huge $expand tree and timing out the server or the client.
- !Ignoring the nextLink and quietly truncating a data extract at the page size.
- !Using a high-privilege user for the integration instead of a least-privilege service account.
How an ERP-grounded AI assistant handles this
A grounded assistant such as ERPray reads the metadata of your Epicor instance to build correct OData URLs with real entity and field names, then runs read-only queries under the caller's own Epicor role and shows the exact request it made. That removes most guesswork about names and filters, and keeps data access inside the same security as a normal user.
Frequently asked questions
What is the difference between Epicor REST v1 and v2?
REST v2 is the newer OData based interface with a consistent URL pattern, API key support and better metadata. Version 1 uses an older URL layout and is being phased out in favour of v2 in Kinetic. New integrations should use v2 and plan to move existing v1 calls.
How do I filter by date in Epicor OData?
Use an ISO style datetime comparison such as ChangeDate gt 2026-01-01T00:00:00Z, or the OData date functions where supported. The exact accepted format depends on the field type and version, so test with $top first. Timezone handling is a common source of off-by-one-day results.
Can I call a BAQ with parameters through REST?
Yes. Call the BAQ through BaqSvc and filter on its output fields with $filter, or use the BAQ's own parameters via the query options your version supports. Filtering on output columns is the most portable approach across Kinetic releases.
Why does my Epicor REST call return an empty list?
Check the company in the URL, the filter value spelling and case, and whether the user's security hides the data. Remove the filter and use $top=5 to confirm any rows return. An empty result with a 200 status is often a filter on the wrong field name or the wrong company.
Related
Fixing Epicor REST v2 API 401 Unauthorized Errors
A 401 Unauthorized calling Epicor's REST v2 (api/v2/odata) endpoint almost always comes down to one of three things: a missing or wrong x-api-key header, valid credentials but an API key scoped to a different company than the one in the URL, or REST services simply not enabled for that endpoint. Confirm the API key exists and is active in Application Studio (or the classic REST API help page), matches the company segment in the URL, and that the account used for Basic auth or OAuth has the right security group.
How-toHow to Add a Calculated Field in an Epicor BAQ
In Epicor Query Builder, open the Phrase Build or Calculated Fields area of the Analyze tab or Display Fields, add a new calculated field with a name and a data type, and write a SQL expression that references columns as Table_Column. Guard against NULL and zero divisors, and use a subquery when the value needs its own aggregation.
AdvancedHow to create and call an Epicor Function
Open Function Studio from the main menu (Customization > Function Studio) or from inside a BPM designer's Call Context widget, define a Library and Function with typed inputs/outputs, write the logic in the C# widget, then compile and test with the built-in test harness before calling it from a BPM directive, a dashboard, or another function.
How-toHow to import data with Epicor DMT (Data Migration Tool)
In DMT, pick the target data type (e.g. Part, Customer, PartWhse), Generate a template from the connected Epicor server so column names match your version's schema exactly, populate the spreadsheet, load it into DMT, run a Pre-Process/validate pass first to surface errors before committing, then Process to write the records.
Error fixFixing Epicor BusinessObjectException: BPM Directive Errors
A BusinessObjectException that says "A Business Process Management (BPM) directive has raised the following error" means a directive on that business object stopped the transaction, either deliberately via a Raise Exception widget or accidentally via an unhandled .NET error in Custom Code. Expand the InnerException on the error dialog to see the real message and the directive name, then open BPM Designer for that object and method to find the widget that fired.
How-toFixing a Slow Epicor BAQ (Business Activity Query)
A slow BAQ in Epicor is usually caused by unindexed join columns, a subquery or calculated field forcing a table scan, or the BAQ pulling far more rows than the dashboard actually displays before filtering client-side. Fix it by reading the SQL Server execution plan Epicor generates, moving filters into the BAQ criteria instead of the dashboard filter panel, and replacing subqueries with joins where possible.
AI for ERPAI for Epicor Kinetic, Beyond What Prism Covers
Add AI to Epicor Kinetic beyond Prism: private LLM over BAQs, BPM data, and REST v2, on-prem or private cloud, with honest guidance on when Prism already covers you.
AI for ERPPrivate AI for Epicor Kinetic Cloud, Outside the Multi-Tenant Boundary
Ground a private LLM on Epicor Kinetic Cloud via BAQs, REST v2, and Data Fabric so sensitive manufacturing data never enters a multi-tenant AI feature.
Stuck on Epicor Kinetic?
Talk to engineers who work inside Epicor Kinetic every week, and who build private AI that answers these questions from your own ERP data.