SAP S/4HANA OData API Capabilities
What are the SAP S/4HANA OData V2 and V4 API capabilities - pagination, batch, deep insert, rate limits?
TL;DR
- Bottom line: SAP S/4HANA exposes 600+ OData APIs (V2 and V4) for CRUD, batch, and deep operations; all new APIs since 2023 are OData V4 via the RAP framework, while existing V2 APIs enter maintenance mode. [src1, src2]
- Key limit: Server-driven pagination is enforced (default ~1,000 records/page on-premise); S/4HANA Cloud limits throughput to ~100 requests/second per endpoint with burst to 200 rps. [src5]
- Watch out for: OData V2 cannot filter or sort on expanded navigation properties — only V4 supports
$expandwith inline$filter,$orderby, and$select. Agents frequently hallucinate V2 having this capability. [src3, src4] - Best for: Real-time CRUD of business objects (sales orders, purchase orders, business partners, materials) with structured query capabilities and SAP Fiori element integration. [src1]
- Authentication: OAuth 2.0 (S/4HANA Cloud), SAML 2.0 with principal propagation, Basic Auth (on-premise dev/test only), or X.509 client certificates (mTLS) for high-security scenarios. [src5]
System Profile
SAP S/4HANA is SAP's flagship ERP system, available in three deployment models: Cloud Public Edition (multi-tenant SaaS), Cloud Private Edition (single-tenant managed), and On-Premise (customer-managed). The OData API surface is the primary programmatic interface, built on the SAP Gateway framework (V2) and the ABAP RESTful Application Programming Model (RAP, V4). This card covers OData-specific capabilities across all deployment models. It does NOT cover SOAP, RFC/BAPI, IDoc, or file-based interfaces, which use fundamentally different protocols and have separate rate limits. [src1, src5]
| Property | Value |
|---|---|
| Vendor | SAP |
| System | SAP S/4HANA 2408 (On-Premise) / 2502 (Cloud) |
| API Surface | OData V2 (SAP Gateway) + OData V4 (RAP) |
| Current Release | 2408 (On-Premise), continuously updated (Cloud) |
| Editions Covered | Cloud Public, Cloud Private, On-Premise |
| Deployment | Cloud / On-Premise / Hybrid |
| API Docs | SAP Business Accelerator Hub |
| Status | GA (V4 strategic direction; V2 maintenance mode) |
API Surfaces & Capabilities
SAP S/4HANA organizes APIs into five categories: Application-to-Application (A2A), Business-to-Business (B2B), Application-to-External (A2X), Custom Extension APIs, and Pre-built Business Process APIs. The OData surface handles the vast majority of integration scenarios. [src5]
| API Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| OData V2 (Gateway) | HTTPS/JSON or Atom/XML | Legacy integrations, existing Fiori apps | ~1,000/page | ICM-configured / ~100 rps (cloud) | Yes | Via $batch |
| OData V4 (RAP) | HTTPS/JSON | New development, Fiori elements | ~1,000/page | ICM-configured / ~100 rps (cloud) | Yes | Via $batch |
| SOAP | HTTPS/XML | Legacy middleware (PI/PO), WS-* | Varies by service | Shared with OData (ICM) | Yes | No |
| RFC/BAPI | SAP proprietary | SAP-to-SAP, ABAP-native | No hard per-call limit | Work process pool | Yes | No |
| IDoc | SAP proprietary/EDI | EDI, async document exchange | 1 document/IDoc | tRFC queue | No (async) | Yes |
OData V2 vs V4 Feature Comparison
| Feature | OData V2 | OData V4 | Notes |
|---|---|---|---|
| Default Format | Atom/XML (JSON optional) | JSON (default) | V4 JSON is lighter, faster to parse |
| $filter | Root entity only | Root + expanded entities | V4: $expand=Items($filter=Amount gt 100) |
| $select | Root entity only | Root + expanded entities | V4: $expand=Items($select=ItemNo,Amount) |
| $orderby | Root entity only | Root + expanded entities | V4 enables sorted expansion |
| $count | $inlinecount=allpages | $count=true or /$count | Different syntax, same purpose |
| $search | Not supported | Supported (free-text) | Depends on CDS annotation @Search |
| Batch ($batch) | Multipart/mixed | JSON-based batch (preferred) | V4 also supports multipart/mixed |
| Deep Insert | CREATE_DEEP_ENTITY | Native via navigation properties | V4 approach is cleaner, atomic |
| Deep Update | Not supported | Limited (OData 4.01 draft) | RAP support still maturing |
| Delta Queries | Not standard | $deltatoken / delta links | Track changes since last query |
| Lambda Operators | Not supported | any() / all() | Filter collections |
| Update Method | POST + X-HTTP-METHOD:MERGE | PATCH (partial) or PUT (full) | V4 follows standard HTTP semantics |
| Enum Types | Not supported | Supported | Strongly typed enumerations |
| Actions/Functions | Function Imports only | Actions + Functions (separate) | V4 has clearer separation |
Rate Limits & Quotas
Per-Request Limits
| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max records per page | ~1,000 (default, configurable) | All OData queries | Server-driven paging via __next (V2) or @odata.nextLink (V4) |
| Max $top value | System-dependent | All queries | On-premise: configurable in Gateway; Cloud: enforced server-side |
| Max $expand depth | 3-4 levels (typical) | All queries | Deep expansion degrades performance exponentially |
| Max request body size | ~50 MB (ICM default) | $batch, POST, PATCH | On-premise: configurable via ICM; Cloud: fixed |
| Max response size | ~50 MB (ICM default) | All responses | On-premise: configurable; Cloud: fixed |
| Request timeout | 600s (Cloud) / configurable (On-Premise) | All requests | On-premise: ICM PROCTIMEOUT parameter |
| CSRF token validity | Session-scoped | All write operations | Fetch via GET with X-CSRF-Token: Fetch |
Rolling / Daily Limits
| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| Request throughput | ~100 rps per API endpoint | Per second | Cloud: ~100 rps baseline, 200 rps burst; On-Premise: ICM thread pool |
| Daily API call quota | No hard daily limit (Cloud: fair use) | 24h | Cloud: throttled under fair-use policy; On-Premise: resource-bound |
| Concurrent HTTP connections | 500 (ICM default) | Per system | On-premise: icm/max_conn; Cloud: managed |
| Max ICM threads | 50 (default) | Per system | On-premise: icm/max_threads; Cloud: auto-scaled |
[src5]
ICM / Gateway Processing Limits (On-Premise)
| Limit Type | Per-Transaction Value | Notes |
|---|---|---|
| ICM connection timeout | 60,000 ms (default) | icm/conn_timeout |
| ICM keep-alive timeout | 300s (default) | icm/keep_alive_timeout |
| Dialog work process time | 600s (default) | rdisp/max_wprun_time |
| Max sockets | 2,048 (default) | icm/max_sockets |
| Memory pipe size | 80 MB (default) | mpi/total_size_MB |
Authentication
| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| OAuth 2.0 (Client Credentials) | S/4HANA Cloud server-to-server | ~12h (configurable) | Yes | Recommended for Cloud; uses SAP BTP XSUAA |
| OAuth 2.0 (SAML Bearer) | User-context propagation (Cloud) | Session-scoped | N/A | Principal propagation from IdP |
| SAML 2.0 | SSO, enterprise IdP integration | Session-scoped | N/A | Used with SAP BTP destinations |
| Basic Authentication | On-premise dev/test only | Session timeout (1800s) | N/A | DO NOT use in production Cloud |
| X.509 Client Certificate | High-security on-premise/Cloud | Certificate validity | N/A | Requires PKI infrastructure |
[src5]
Authentication Gotchas
- CSRF tokens are mandatory for all write operations. Fetch via GET with
X-CSRF-Token: Fetchheader, then include in subsequent writes. Tokens are session-scoped. [src1] - OAuth 2.0 on S/4HANA Cloud requires SAP BTP as the OAuth authorization server (XSUAA). You cannot use a third-party OAuth provider directly. [src5]
- S/4HANA Cloud API endpoints require Communication Arrangements configured in Fiori launchpad. Without the arrangement, the API returns 403 even with valid credentials. [src2]
Constraints
- Server-driven pagination is non-negotiable: the backend controls page size and clients must follow
__next(V2) or@odata.nextLink(V4). There is no client-side override. [src6] - OData V2 APIs are in maintenance mode — SAP creates all new APIs exclusively as OData V4 via the RAP framework. Plan for V4 migration. [src2, src3]
- Deep update is not supported in OData V2 and has only limited support in V4 (OData 4.01 spec). Use $batch with individual operations in a changeset instead. [src3, src4]
$expanddepth beyond 3-4 levels causes severe performance degradation and may time out. Design APIs to minimize expansion depth. [src1]- S/4HANA Cloud does not allow custom OData V2 services — only RAP-based V4 services can be created. [src2]
- Communication Arrangements must be configured before any Cloud API is accessible — missing arrangements return 403. [src2]
- ETag-based optimistic locking is enforced on many APIs. Omitting
If-Matchon PATCH/DELETE results in 428 or 412. [src7]
Integration Pattern Decision Tree
START — User needs to integrate with SAP S/4HANA via OData
|-- Which OData version?
| |-- New project (greenfield)? -> OData V4 (RAP-based)
| +-- Existing project? -> Check if V4 equivalent exists on api.sap.com
|-- What's the integration pattern?
| |-- Real-time (individual records, <1s)
| | |-- Single entity CRUD? -> Direct GET/POST/PATCH/DELETE
| | |-- Multiple related entities?
| | | |-- CREATE? -> Deep Insert (V4 preferred)
| | | +-- UPDATE? -> $batch with changeset
| | +-- Need notifications? -> SAP Event Mesh (Cloud) / polling (On-Premise)
| |-- Batch/Bulk (scheduled, high volume)
| | |-- < 1,000 records? -> Simple loop with individual calls
| | |-- 1,000-50,000 records? -> $batch (100-500 ops per batch)
| | +-- > 50,000 records? -> IDoc or file-based (not OData)
| |-- Event-driven -> Business Events (Cloud) / polling with $filter (On-Premise)
| +-- File-based -> Use IDoc/BAPI, not OData
|-- Which direction?
| |-- Inbound -> Verify CSRF token + ETag handling
| |-- Outbound -> Use $select to minimize payload
| +-- Bidirectional -> Conflict resolution via ETags + LastChangedAt
+-- Deployment model?
|-- Cloud -> Communication Arrangement + OAuth 2.0 (XSUAA)
|-- Cloud Private -> Similar to Cloud; custom namespaces allowed
+-- On-Premise -> Basic Auth or X.509; configure ICM for throughput
Quick Reference
Common OData Operations
| Operation | V2 Method | V4 Method | Notes |
|---|---|---|---|
| List entities | GET /{EntitySet} | GET /{EntitySet} | Add $top, $skip, $filter |
| Get by key | GET /{EntitySet}('{key}') | GET /{EntitySet}('{key}') | Single quotes around string keys |
| Create | POST /{EntitySet} | POST /{EntitySet} | CSRF token required |
| Update (partial) | POST + X-HTTP-METHOD: MERGE | PATCH | V2 uses MERGE; V4 uses PATCH |
| Update (full) | PUT | PUT | Replaces entire entity |
| Delete | DELETE | DELETE | ETag in If-Match header |
| Deep insert | POST with nested JSON | POST with navigation | Atomic: all or nothing |
| Batch | POST /$batch (multipart) | POST /$batch (JSON) | Group related ops in changesets |
| Count | GET /$count | GET /$count | Returns plain integer |
| Metadata | GET /$metadata | GET /$metadata | Full EDMX/CSDL schema |
Pagination Quick Reference
| Mechanism | OData V2 | OData V4 | When to Use |
|---|---|---|---|
| Client-driven | $top=100&$skip=200 | $top=100&$skip=200 | Small datasets, known total count |
| Server-driven | __next property | @odata.nextLink | Large datasets (mandatory) |
| $skiptoken | In __next URL | In nextLink URL | Efficient server-side cursor |
| Inline count | $inlinecount=allpages | $count=true | Get total record count |
[src6]
Step-by-Step Integration Guide
1. Discover and Select the API
Browse the SAP Business Accelerator Hub at api.sap.com to find the specific OData service for your business object. [src1, src2]
# Browse S/4HANA Cloud V4 APIs: https://api.sap.com/products/SAPS4HANACloud/apis/ODATAV4
# Browse S/4HANA On-Premise V2 APIs: https://api.sap.com/package/S4HANAOPAPI/odata
# Example: Business Partner API — Service: API_BUSINESS_PARTNER
Verify: Search for your business object and confirm API status is "Active" (not Deprecated).
2. Authenticate and Obtain Access Token
For S/4HANA Cloud, configure a Communication Arrangement and use OAuth 2.0. For on-premise, use Basic Auth or X.509 certificates. [src5]
# S/4HANA Cloud — OAuth 2.0 Client Credentials Flow
curl -X POST "https://{subdomain}.authentication.{region}.hana.ondemand.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id={id}&client_secret={secret}"
# On-Premise — Basic Auth
curl -X GET "https://{host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=10" \
-u "{user}:{pass}" -H "Accept: application/json"
Verify: HTTP 200 with d.results (V2) or value (V4) array.
3. Fetch CSRF Token for Write Operations
All OData write operations require a CSRF token. Fetch it before any modification request. [src1]
curl -X GET "https://{host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=1" \
-H "Authorization: Bearer {token}" \
-H "X-CSRF-Token: Fetch" -H "Accept: application/json" -v 2>&1 | grep csrf
Verify: Response headers contain x-csrf-token: {non-empty-value}.
4. Handle Pagination
Follow server-driven pagination by iterating through nextLink URLs until no more pages exist. [src6]
def fetch_all_pages(base_url, headers, params=None):
all_results = []
url = base_url
while url:
response = requests.get(url, headers=headers, params=params)
data = response.json()
if 'value' in data: # V4
all_results.extend(data['value'])
url = data.get('@odata.nextLink')
elif 'd' in data: # V2
all_results.extend(data['d'].get('results', []))
url = data['d'].get('__next')
else:
break
params = {} # nextLink includes all params
return all_results
Verify: len(all_results) matches the $count value.
Code Examples
Python: Read Business Partners with Pagination
# Input: SAP S/4HANA OData V2 credentials, filter criteria
# Output: List of business partner records matching criteria
import requests
base_url = "https://{host}/sap/opu/odata/sap/API_BUSINESS_PARTNER"
headers = {"Authorization": "Basic {base64_credentials}", "Accept": "application/json"}
params = {
"$filter": "CreationDate gt datetime'2025-01-01T00:00:00'",
"$select": "BusinessPartner,BusinessPartnerFullName,CreationDate",
"$top": "500", "$inlinecount": "allpages",
}
all_partners = []
response = requests.get(f"{base_url}/A_BusinessPartner", headers=headers, params=params)
data = response.json()
total = int(data['d']['__count'])
all_partners.extend(data['d']['results'])
while '__next' in data['d']:
response = requests.get(data['d']['__next'], headers=headers)
data = response.json()
all_partners.extend(data['d']['results'])
print(f"Fetched {len(all_partners)} of {total} business partners")
JavaScript/Node.js: Deep Insert Sales Order (V4)
// Input: SAP S/4HANA Cloud OData V4 endpoint, OAuth token
// Output: Created sales order with line items
const { executeHttpRequest } = require("@sap-cloud-sdk/http-client");
const response = await executeHttpRequest(
{ destinationName: "S4HANA_CLOUD" },
{
method: "POST",
url: "/sap/opu/odata4/sap/api_salesorder/srvd_a2x/sap/salesorder/0001/SalesOrder",
headers: { "Content-Type": "application/json" },
data: {
SalesOrderType: "OR", SalesOrganization: "1710",
DistributionChannel: "10", SoldToParty: "10100001",
_Item: [
{ Material: "TG11", RequestedQuantity: "10", RequestedQuantityUnit: "PC" },
{ Material: "TG12", RequestedQuantity: "5", RequestedQuantityUnit: "PC" },
],
},
}
);
console.log(`Created: ${response.data.SalesOrder}`);
cURL: $batch with Multiple Reads (V4 JSON)
# Input: OAuth access token, S/4HANA Cloud endpoint
# Output: Multiple entity responses in single HTTP round-trip
curl -X POST "https://{tenant}.s4hana.ondemand.com/.../$batch" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"requests":[{"id":"1","method":"GET","url":"A_BusinessPartner?$top=5"},{"id":"2","method":"GET","url":"A_BusinessPartnerAddress?$top=5"}]}'
Data Mapping
OData Property Type Reference
| OData V2 Type | OData V4 Type | SAP ABAP Type | Notes |
|---|---|---|---|
| Edm.String | Edm.String | CHAR, NUMC | Max length from ABAP data element |
| Edm.DateTime | Edm.DateTimeOffset | DATS + TIMS | V2: /Date(ms)/; V4: ISO 8601 |
| N/A | Edm.Date | DATS | V4 only — date without time |
| N/A | Edm.TimeOfDay | TIMS | V4 only — time without date |
| Edm.Decimal | Edm.Decimal | CURR, QUAN, DEC | String-encoded to avoid precision loss |
| Edm.Int32 | Edm.Int32 | INT4 | Standard 32-bit integer |
| Edm.Boolean | Edm.Boolean | ABAP_BOOL | true/false |
| Edm.Guid | Edm.Guid | SYSUUID_X16 | UUID format |
Data Type Gotchas
- OData V2 serializes
Edm.DateTimeas/Date(1234567890000)/(Unix ms). V4 uses ISO 8601 (2026-03-01T00:00:00Z). Middleware must parse both. [src3] Edm.Decimalvalues are serialized as strings in both V2 and V4. Use a decimal library, notparseFloat(). [src4]- SAP NUMC fields appear as
Edm.Stringand must be zero-padded to full length (e.g.,"0000001000"for 10-char). [src1] - Date fields with no value may appear as
/Date(0)/(1970-01-01) in V2 — treat as null. [src3]
Error Handling & Failure Points
Common Error Codes
| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 400 | Bad Request | Malformed OData URL, invalid $filter, wrong types | Validate query syntax; check EDMX metadata |
| 401 | Unauthorized | Invalid/expired credentials or token | Re-authenticate; verify Communication Arrangement |
| 403 | Forbidden | Missing CSRF token, insufficient auth, no Comm. Arrangement | Fetch CSRF; check roles; verify arrangement |
| 404 | Not Found | Wrong service URL, inactive service | Activate in /IWFND/MAINT_SERVICE (On-Premise) |
| 412 | Precondition Failed | ETag mismatch | Re-read entity, retry with current ETag |
| 428 | Precondition Required | Missing If-Match header | Include If-Match: {etag} header |
| 429 | Too Many Requests | Rate limit exceeded (Cloud) | Exponential backoff; check Retry-After |
| 500 | Internal Server Error | ABAP runtime error, data inconsistency | Check /IWFND/ERROR_LOG; review ABAP dump (ST22) |
Failure Points in Production
- Pagination loop never terminates: Some V2 services return
__nexton the last page if total count is an exact multiple of page size. Fix:Always check if results array is empty before following __next. [src6] - CSRF token expires mid-batch: Long-running $batch operations can outlive the session. Fix:
Fetch fresh CSRF token immediately before each $batch POST. [src1] - Decimal precision loss:
JSON.parse()converts Decimal strings to Numbers, losing precision. Fix:Use decimal.js or keep as strings. [src3] - $expand performance cliff: Expanding 3+ levels causes exponential response size and timeouts. Fix:
Fetch parent first, query children with $filter on parent key. [src1] - Zero-padded NUMC validation: Sending "1000" instead of "0000001000" returns cryptic 400 error. Fix:
Check EDMX maxLength and zero-pad. [src1] - Missing Communication Arrangement: Cloud API calls return 403 with generic message. Fix:
Verify arrangement in Fiori > Manage Communication Arrangements. [src2]
Anti-Patterns
Wrong: Using $skip for Large Dataset Extraction
# BAD — $skip forces database to scan and discard rows
for offset in range(0, total_count, page_size):
response = requests.get(f"{base_url}/A_SalesOrder?$skip={offset}&$top={page_size}")
# Page 1000+ takes exponentially longer
Correct: Follow Server-Driven Pagination
# GOOD — $skiptoken uses efficient cursor-based pagination
url = f"{base_url}/A_SalesOrder?$top={page_size}"
while url:
data = requests.get(url, headers=headers).json()
process_page(data['d']['results'])
url = data['d'].get('__next') # Contains $skiptoken
Wrong: Individual API Calls in Loop for Bulk
# BAD — N separate HTTP round-trips
for partner in partners:
requests.patch(f"{base_url}/A_BusinessPartner('{partner['id']}')", json=partner['data'])
Correct: Use $batch with Changesets
# GOOD — group updates into $batch (100-500 per batch)
for i in range(0, len(partners), 200):
batch = partners[i:i + 200]
requests.post(f"{base_url}/$batch", data=build_batch(batch))
# 10,000 partners = 50 HTTP round-trips
Wrong: Expanding All Navigation Properties
# BAD — massive response sizes, potential timeout
requests.get(f"{base_url}/A_SalesOrder('1000000')?$expand=to_Item,to_Item/to_PricingElement,to_Partner")
Correct: Selective $expand with $select
# GOOD — V4: filter and select on expanded entities
requests.get(f"{base_url_v4}/SalesOrder('1000000')?$expand=_Item($select=SalesOrderItem,Material)&$select=SalesOrder,SoldToParty")
Common Pitfalls
- Not activating OData services (On-Premise): Most services ship inactive. Activate in
/IWFND/MAINT_SERVICEbefore API calls. [src1] - Ignoring Content-Type differences: V2 defaults to Atom/XML; V4 to JSON. Always set
Accept: application/jsonexplicitly. [src3] - Wrong $filter date format: V2 uses
datetime'2026-01-01T00:00:00'; V4 uses2026-01-01. Mixing produces 400 errors. [src3, src4] - Hardcoding session timeouts: SAP timeouts are admin-configurable. Handle 401/403 by re-authenticating. [src5]
- Not handling partial $batch success: Non-changeset batch ops can have individual failures. Parse each sub-response. [src3]
- Using $orderby without index support: Sorting non-indexed fields causes full table scans on large tables. [src1]
Diagnostic Commands
# Check if OData service is active (On-Premise)
curl -s "https://{host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/$metadata" \
-u "{user}:{pass}" -H "Accept: application/xml" | head -5
# Expected: <?xml ...><edmx:Edmx ...>
# Test CSRF token fetch
curl -v "https://{host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=1" \
-u "{user}:{pass}" -H "X-CSRF-Token: Fetch" -H "Accept: application/json" 2>&1 | grep csrf
# Expected: x-csrf-token: {token_value}
# Check S/4HANA Cloud API health
curl -s -o /dev/null -w "%{http_code}" \
"https://{tenant}.s4hana.ondemand.com/.../$metadata" -H "Authorization: Bearer {token}"
# Expected: 200 (healthy), 401 (auth), 403 (arrangement), 503 (maintenance)
# ICM parameters (On-Premise — SAP GUI transaction RZ10/RZ11)
# icm/max_conn, icm/max_threads, icm/conn_timeout, PROCTIMEOUT
Version History & Compatibility
| S/4HANA Release | Date | Status | Key Changes | Migration Notes |
|---|---|---|---|---|
| 2408 (On-Premise) | 2024-10 | Current | New V4 APIs for manufacturing, warehousing | V2 still supported; V4 recommended |
| 2025 (Private Cloud) | 2025-02 | Current | Service Order V2 deprecated; V4 successor | Migrate Service Order to V4 |
| 2023 (On-Premise) | 2023-10 | Supported | RAP-based V4 APIs mainstream | Minimum for broad V4 coverage |
| 1909 (On-Premise) | 2019-09 | End of Maintenance (2027) | Last pre-RAP release | V2 only; plan migration to 2023+ |
Deprecation Policy
SAP follows a structured API deprecation lifecycle: APIs are marked "Deprecated" on SAP API Business Hub with a successor identified, then enter a minimum 2-year grace period. Starting 2025, all new APIs are OData V4 exclusively. Check SAP Note 2836302 for the latest deprecation schedule. [src2]
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Real-time CRUD of business objects | Bulk migration > 50,000 records | IDoc flat files or SAP Data Services |
| Fiori app backend or custom UI5 | Real-time event streaming | SAP Event Mesh + Business Events |
| Structured queries ($filter, $expand) | Unstructured full-text search | SAP Enterprise Search / HANA FTS |
| Standard objects with published APIs | Custom Z-tables without CDS views | Build RAP service first |
| Middleware integration (MuleSoft, Boomi) | Large binary file upload/download | SAP DMS / CMS |
| Cross-system integration (JSON) | SAP-to-SAP in same landscape | RFC/BAPI (lower overhead) |
Important Caveats
- S/4HANA Cloud and On-Premise have different API catalogs — not all Cloud APIs exist On-Premise and vice versa. Always verify on api.sap.com. [src1, src2]
- Rate limits differ significantly between Cloud (SAP-managed, fair-use) and On-Premise (customer-configurable ICM). Benchmarks from one model do not apply to the other. [src5]
- SAP's V2-to-V4 migration is ongoing — some business objects only have V2 APIs as of early 2026. Check api.sap.com before assuming V4 availability. [src2]
- Throughput figures (~100 rps, burst 200 rps) for Cloud are approximate and subject to fair-use throttling. Always implement retry logic with backoff. [src5]
- Rate limits are subject to change with each release; always verify against current release notes.