Module in this article: Integration Flow Helper
OData Receiver in SAP CPI: $select, $filter, $expand and $orderby Without Typos
Write OData query options for the SAP CPI OData receiver: $select, $filter, $expand, $orderby and $top with examples, the V2 and V4 differences, and the typos that cause HTTP 400.

The OData receiver adapter in SAP CPI turns a Resource Path and a line of query options into a call to an OData service, such as an SAP S/4HANA API. The query options decide which fields, rows and related entities come back, and a single misspelled property name is enough for the service to reject the call.
This article walks through $select, $filter, $expand and $orderby on an example, the differences between OData V2 and V4 that trip people up, and how to keep the names right.
Where query options go in the OData receiver
In the adapter's Processing tab you choose the operation, then set the Resource Path, which is the entity set or the entity you call, and the Query Options, which is everything after the ? in the URL. The adapter's query wizard can build both from the service metadata and stores that metadata, the EDMX file, in the iFlow's resources.
Query options can contain Simple expressions, so values can come from the message, for example $filter=SoldToParty eq '${property.customerId}'. The example below reads sales orders with their items from API_SALES_ORDER_SRV, an OData V2 service:
Resource Path: A_SalesOrder
Query Options: $select=SalesOrder,SoldToParty,CreationDate,to_Item/SalesOrderItem,to_Item/Material
&$expand=to_Item
&$filter=SoldToParty eq '${property.customerId}' and CreationDate ge datetime'2026-01-01T00:00:00'
&$orderby=CreationDate desc
&$top=50In the adapter the query options are one line; they are split here only to be readable.
The four options, one by one
| Option | What it does | Example |
|---|---|---|
$select | Returns only the listed properties, which keeps responses small | $select=SalesOrder,SoldToParty |
$filter | Returns only the rows that match a condition | $filter=SalesOrderType eq 'OR' |
$expand | Includes related entities through a navigation property | $expand=to_Item |
$orderby | Sorts the rows, ascending by default | $orderby=CreationDate desc |
$top and $skip limit and page the result. In OData V2, a property of an expanded entity is selected with a path, as in to_Item/Material, and the navigation property must also be listed in $expand.
$filter uses its own operators: eq, ne, gt, ge, lt, le, joined with and, or and not. Text values go in single quotes, and dates need the literal form the service version expects.
OData V2 and V4 are not written the same way
The OData adapter supports both versions, and many SAP APIs exist in both. Copying a query from one version to the other is a common source of errors:
OData V2: $inlinecount=allpages
$filter=substringof('Smith',CustomerName)
$filter=CreationDate ge datetime'2026-01-01T00:00:00'
OData V4: $count=true
$filter=contains(CustomerName,'Smith')
$filter=CreatedAt ge 2026-01-01T00:00:00Z
$expand=_Item($select=Product,Quantity)- Counting rows:
$inlinecount=allpagesin V2,$count=truein V4. - Searching inside text:
substringof('Smith',CustomerName)in V2,contains(CustomerName,'Smith')in V4. Note that the arguments are in the opposite order. - Dates: V2 writes
datetime'2026-01-01T00:00:00'; V4 writes the value without a prefix. - Expanded properties: V2 selects them with a path; V4 nests the options inside the expand, as in
$expand=_Item($select=Product,Quantity).
The typos that cause HTTP 400
When a query option is wrong, the service usually answers with HTTP 400 and names the part it could not read. The mistakes behind it are almost always the same:
- Wrong case: OData names are case-sensitive, so
soldToPartyis notSoldToParty. - An entity set where a navigation property belongs:
$expand=A_SalesOrderIteminstead of$expand=to_Item. - A property that exists on another entity, or only in another version of the service.
- Missing quotes around text values, or a V2 date literal sent to a V4 service.
- A separator problem: options are joined with
&, and every option name starts with$.
Expressions add their own risk: if ${property.customerId} is empty, the filter becomes SoldToParty eq '' and silently returns no rows instead of an error. The Simple expressions article covers how those values are read.
Keep the query and the service in step
- Treat the EDMX in the iFlow's resources as the source of truth for names, and refresh it when the service changes.
- Select only the fields you map; a smaller response is faster and easier to read in a trace.
- For large reads, use the adapter's option to process in pages instead of one huge response.
- Test the query once with the exact values a real message will put in it.
Calling a REST API instead of an OData service? The article on Postman and OpenAPI covers the HTTP receiver.
How Kitegra helps
The Kitegra browser extension reads the EDMX that your iFlow already holds for an OData receiver channel. In the designer it suggests the Resource Path and the property and navigation names for $select, $filter, $expand and $orderby from that EDMX, so you pick names instead of typing them.
Its lint panel flags OData names that the EDMX does not contain, along with unclosed ${ and headers or properties that nothing sets. If a channel gets no suggestions, check that its EDMX is in the iFlow's resources. It never saves, deploys or edits an iFlow's content, and your tenant changes only when you click one of four named actions, which can be switched off.
To check what the OData service really answered, trace the message step by step and read the body after the call.
Without an account it works on 5 iFlows per module. The Free plan covers 50, Starter 100 and Pro 300; Business has no limit. Compare the plans.
Try it in your next iFlow
Read what Kitegra does in the SAP Integration Suite designer, or see it next to its modules.
Frequently asked questions
Where do I put $filter in the SAP CPI OData adapter?
In the Query Options field of the receiver channel's Processing tab, together with the other options, for example $select=SalesOrder&$filter=SalesOrderType eq 'OR'.
Can I use a header or property inside an OData filter?
Yes. Query options accept Simple expressions, for example $filter=SoldToParty eq '${property.customerId}'. If the value is empty the filter returns no rows, so check it first.
What is the difference between $inlinecount and $count?
$inlinecount=allpages is OData V2; $count=true is the OData V4 form. Both return the total number of matching rows with the result.
Why does substringof fail on my service?
substringof exists only in OData V2. An OData V4 service uses contains(Property,'text'), with the arguments in the opposite order.
Why does the service answer with HTTP 400?
Usually a name in the query does not exist on that entity, often because of case, a navigation property confused with an entity set, or syntax from the other OData version.
Where does SAP CPI keep the OData metadata?
The query wizard stores the service's EDMX file in the iFlow's resources. Refresh it when the service changes so the names stay correct.