Oracle Fusion Cloud REST API Deep Dive: 499-Record Limit, Pagination, Expand & Field Filtering

Type: ERP Integration System: Oracle Fusion Cloud Applications (REST Framework v4) Confidence: 0.88 Sources: 7 Verified: 2026-03-09 Freshness: evolving

TL;DR

System Profile

Oracle Fusion Cloud Applications (also marketed as Oracle Cloud ERP, Oracle Cloud SCM, Oracle Cloud HCM, and Oracle CX) exposes a comprehensive REST API surface for real-time integration with business objects across all pillars. The REST API is built on the Oracle ADF Business Components framework and has evolved through multiple framework versions (v1 through v4). This card covers the REST API surface specifically -- not SOAP, BICC, or FBDI. All Oracle Fusion Cloud editions share the same REST API capabilities.

PropertyValue
VendorOracle
SystemOracle Fusion Cloud Applications (ERP, SCM, HCM, CX)
API SurfaceREST (JSON over HTTPS)
Current API VersionFramework v4 (available since 24B release)
Editions CoveredAll editions (no API differences by edition)
DeploymentCloud (SaaS only)
API DocsOracle Fusion Cloud REST API Reference
StatusGA (strategic direction -- SOAP deprecated for new development)

API Surfaces & Capabilities

Oracle Fusion Cloud offers multiple API surfaces for different integration patterns. REST is the primary modern surface, but understanding when to use alternatives is critical. [src5, src6]

API SurfaceProtocolBest ForMax Records/RequestRate LimitReal-time?Bulk?
REST API (fscmRestApi)HTTPS/JSONReal-time CRUD, <10K records499 per GET page~5,000 calls/hr/userYesNo
SOAP Web ServicesHTTPS/XMLLegacy integrations, metadata opsVaries by serviceShared with RESTYesNo
FBDI (File-Based Data Import)CSV in ZIP via RESTBulk inbound imports, data migration100,000 per import job5 concurrent importsNoYes
BICC (BI Cloud Connector)Bulk extract via UCMLarge outbound data extractionMillions (full/incremental)ScheduledNoYes
BIP (BI Publisher)SOAP/RESTReport-based data extractionReport-dependentReport queue limitsNoPartial
Business EventsEvent subscriptionEvent-driven integration via OICN/A (push)Event queue limitsYesN/A

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Max records per GET response499REST API GET (all resources)Hard cap. Setting limit > 499 silently returns 499 and misreports totalCount
Default page size25REST API GET (no limit param)Override with ?limit=499 for efficiency
Max POST records (recommended)500REST API POST/batch createsPerformance degrades significantly above this threshold
Max request payload size~10 MBREST API POST/PATCHLarger payloads may timeout or be rejected by WAF
Max FBDI ZIP file size250 MBFile-Based Data ImportIndividual CSV files within ZIP can be up to 1 GB
Max FBDI records per import100,000File-Based Data ImportSplit larger datasets into multiple import jobs
Max concurrent FBDI imports5File-Based Data ImportAdditional imports queue behind running jobs

Rolling / Daily Limits

Limit TypeValueWindowEdition Differences
API calls per user~5,000Per hour (rolling)No edition differences -- same for all Fusion Cloud tenants
Total tenant API callsFair-use throttlingRolling windowOracle reserves right to throttle tenants impacting shared infrastructure
FBDI concurrent imports5Per tenantShared across all users and integration flows
BIP concurrent reportsQueue-basedPer tenantLong-running reports can block the queue
BICC extract jobsScheduledPer tenantTypically 1-2 concurrent extract streams

Authentication

Oracle Fusion Cloud REST APIs use a global Oracle Web Services Manager (OWSM) security policy called Multi Token Over SSL. [src4]

FlowUse WhenToken LifetimeRefresh?Notes
Basic Auth over SSLTesting, simple integrationsPer requestNoUsername:password in base64; not recommended for production
OAuth 2.0 (Client Credentials)Server-to-server production integrationsConfigurable (~1h)YesRecommended; requires Oracle IAM confidential app setup
OAuth 2.0 (JWT Bearer)Automated integrations, service accountsJWT-definedNew JWT per requestRequires certificate registration in Oracle IAM
SAML 2.0 BearerSSO federation, cross-cloud identityAssertion validityNoToken passed in HTTP Authorization header
OAuth 2.0 (3-legged)User-context operations, delegated accessAccess: ~1h, Refresh: until revokedYesStandard authorization code flow

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START -- User needs to integrate with Oracle Fusion Cloud
+-- What's the data flow direction?
|   +-- OUTBOUND (reading from Fusion)?
|   |   +-- How many records per extraction?
|   |   |   +-- < 1,000 records --> REST API with limit=499 + offset pagination
|   |   |   +-- 1,000 - 10,000 records --> REST API pagination loop (2-20 API calls)
|   |   |   +-- 10,000 - 100,000 records --> Consider BIP reports or BICC incremental
|   |   |   +-- > 100,000 records --> BICC (mandatory for large extracts)
|   |   +-- Need real-time or scheduled?
|   |       +-- Real-time --> REST API with LastUpdateDate filter for delta
|   |       +-- Scheduled --> BICC incremental extract
|   +-- INBOUND (writing to Fusion)?
|   |   +-- How many records per load?
|   |   |   +-- < 500 records --> REST API POST/PATCH
|   |   |   +-- 500 - 100,000 records --> FBDI (file-based import via REST upload)
|   |   |   +-- > 100,000 records --> FBDI with split files
|   |   +-- Need immediate confirmation?
|   |       +-- YES --> REST API (synchronous response)
|   |       +-- NO --> FBDI (asynchronous, poll for status)
|   +-- EVENT-DRIVEN (react to changes in Fusion)?
|       +-- Business Events available for your object?
|       |   +-- YES --> Subscribe via OIC ERP Cloud Adapter
|       |   +-- NO --> REST API polling with LastUpdateDate filter
|       +-- Need guaranteed delivery?
|           +-- YES --> OIC + dead letter queue
|           +-- NO --> REST polling with retry
+-- Error tolerance?
    +-- Zero-loss required --> Idempotent design + FBDI (built-in per-record error reporting)
    +-- Best-effort --> REST with retry on 429/5xx

Quick Reference

REST API Endpoint Reference

OperationMethodEndpoint PatternPayloadNotes
List resourcesGET/{apiBase}/resources/{version}/{resource}N/AReturns max 499 records; use offset for pagination
Get single recordGET/{apiBase}/resources/{version}/{resource}/{id}N/AReturns full record with expand support
Create recordPOST/{apiBase}/resources/{version}/{resource}JSONReturns created record with generated ID
Update recordPATCH/{apiBase}/resources/{version}/{resource}/{id}JSONPartial update; only send changed fields
Replace recordPUT/{apiBase}/resources/{version}/{resource}/{id}JSONFull replacement; all fields required
Delete recordDELETE/{apiBase}/resources/{version}/{resource}/{id}N/APermanent; no soft-delete via API
Query with filterGET/{apiBase}/resources/{version}/{resource}?q=exprN/Av1: q=field=value; v2+: rowmatch
Expand childrenGET/{apiBase}/resources/{version}/{resource}?expand={child}N/AFetches child resources inline
Select fieldsGET/{apiBase}/resources/{version}/{resource}?fields={f1,f2}N/AReduces payload size; improves performance

Common API Base URLs

PillarAPI Base PathExample
Financials/SCM/fscmRestApihttps://{host}/fscmRestApi/resources/11.13.18.05/invoices
HCM/hcmRestApihttps://{host}/hcmRestApi/resources/11.13.18.05/workers
CRM/CX/crmRestApihttps://{host}/crmRestApi/resources/11.13.18.05/accounts
Common/commonRestApihttps://{host}/commonRestApi/resources/11.13.18.05/users

Key Query Parameters

ParameterPurposeExampleFramework Version
limitRecords per page (max 499)?limit=499All
offsetSkip N records for pagination?offset=499All
qFilter expression?q=Status=APPROVEDAll (syntax varies)
fieldsSelect specific fields?fields=InvoiceId,InvoiceNumberAll
expandInclude child resources?expand=invoiceLinesAll
onlyDataExclude metadata wrapper?onlyData=truev2+
orderBySort results?orderBy=CreationDate:descv2+
totalResultsInclude total count?totalResults=truev2+
finderUse predefined query finder?finder=FindByStatus;Status=OPENAll

Step-by-Step Integration Guide

1. Authenticate and obtain access token

Set up OAuth 2.0 client credentials flow with Oracle IAM for production integrations. [src4]

# OAuth 2.0 Client Credentials -- obtain access token
curl -s -X POST \
  "https://YOUR_IDCS_DOMAIN.identity.oraclecloud.com/oauth2/v1/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -d "grant_type=client_credentials&scope=urn:opc:resource:consumer::all"

Verify: Response contains access_token and expires_in fields

2. Query a resource with pagination

Fetch records using the 499-record pagination pattern. Always use hasMore to control the loop. [src2, src3]

# Fetch first page of invoices (499 records max)
curl -s -X GET \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices?limit=499&offset=0&fields=InvoiceId,InvoiceNumber,InvoiceAmount" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Verify: Check count and hasMore fields in response

3. Implement full pagination loop

Loop through all pages using offset increments until hasMore is false. [src3]

all_records = []
offset = 0
while True:
    params = {"limit": 499, "offset": offset, "onlyData": "true"}
    resp = requests.get(url, headers=headers, params=params)
    data = resp.json()
    all_records.extend(data.get("items", []))
    if not data.get("hasMore", False):
        break
    offset += 499

Verify: len(all_records) matches total record count in Oracle Fusion

4. Use expand and field filtering for efficiency

Reduce payload size by selecting only needed fields and expanding child resources inline. [src1]

# Fetch invoice with expanded lines and selected fields
curl -s -X GET \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/12345?\
fields=InvoiceId,InvoiceNumber&expand=invoiceLines(fields=LineNumber,LineAmount)" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Verify: Response includes invoiceLines nested under the invoice

5. Create a record via POST

Insert a new business object. [src7]

# Create a new invoice
curl -s -X POST \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"InvoiceNumber":"INV-2026-001","InvoiceAmount":15000.00,"InvoiceCurrency":"USD"}'

Verify: HTTP status 201 -- response body contains system-generated InvoiceId

6. Handle rate limiting and errors

Implement exponential backoff for 429 and 5xx responses. [src7]

for attempt in range(max_retries):
    resp = requests.get(url, headers=headers, timeout=60)
    if resp.status_code == 200:
        return resp.json()
    elif resp.status_code == 429:
        time.sleep(min(2 ** attempt * 2, 120))
        continue
    elif resp.status_code >= 500:
        time.sleep(min(2 ** attempt * 3, 180))
        continue

Verify: Test with invalid endpoint returns 404; invalid auth returns 401

Code Examples

Python: Paginated extraction of approved invoices

# Input:  Oracle Fusion Cloud credentials, business unit filter
# Output: Complete list of approved invoices as Python dicts

import requests, time

class OracleFusionClient:
    def __init__(self, host, client_id, client_secret, idcs_domain):
        self.host = host
        self.token_url = f"https://{idcs_domain}.identity.oraclecloud.com/oauth2/v1/token"
        self.auth = (client_id, client_secret)
        self.access_token = None

    def authenticate(self):
        resp = requests.post(self.token_url, auth=self.auth,
            data={"grant_type": "client_credentials",
                  "scope": "urn:opc:resource:consumer::all"}, timeout=30)
        resp.raise_for_status()
        self.access_token = resp.json()["access_token"]

    def get_all_records(self, resource, query=None, fields=None):
        url = f"https://{self.host}/fscmRestApi/resources/11.13.18.05/{resource}"
        headers = {"Authorization": f"Bearer {self.access_token}"}
        all_items, offset = [], 0
        while True:
            params = {"limit": 499, "offset": offset, "onlyData": "true"}
            if query: params["q"] = query
            if fields: params["fields"] = fields
            resp = requests.get(url, headers=headers, params=params, timeout=60)
            if resp.status_code == 429:
                time.sleep(30); continue
            resp.raise_for_status()
            data = resp.json()
            all_items.extend(data.get("items", []))
            if not data.get("hasMore", False): break
            offset += 499
        return all_items

JavaScript/Node.js: Fetch with expand and field filtering

// Input:  Oracle Fusion Cloud credentials, resource name
// Output: Records with expanded child resources

const https = require('https');

async function fetchWithExpand(host, token, resource, id, expandChild, fields) {
  let url = `https://${host}/fscmRestApi/resources/11.13.18.05/${resource}/${id}`;
  const params = new URLSearchParams();
  if (fields) params.set('fields', fields);
  if (expandChild) params.set('expand', expandChild);
  params.set('onlyData', 'true');
  url += '?' + params.toString();

  return new Promise((resolve, reject) => {
    const req = https.get(url, {
      headers: { 'Authorization': `Bearer ${token}` }
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        if (res.statusCode === 200) resolve(JSON.parse(data));
        else reject(new Error(`HTTP ${res.statusCode}: ${data}`));
      });
    });
    req.on('error', reject);
  });
}

cURL: Common diagnostic queries

# Input:  Access token, Oracle Fusion Cloud host
# Output: JSON responses for common API operations

# Discover available resources
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -50

# Get resource metadata
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/describe" \
  -H "Authorization: Bearer $TOKEN"

# Pagination test -- check hasMore behavior
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices?\
limit=2&totalResults=true" -H "Authorization: Bearer $TOKEN"

Data Mapping

REST API Field Type Reference

Oracle Fusion TypeJSON TypeAPI FormatTransform NotesGotcha
DATEStringYYYY-MM-DDNo timezone infoOracle stores as UTC internally; display depends on user timezone
DATETIME/TIMESTAMPStringISO 8601 with TZAlways include timezone offsetAvoid ambiguity by including +00:00
NUMBERNumberDecimalNo roundingCurrency amounts may have 2-6 decimal places
VARCHAR2StringUTF-8Direct mappingMax length varies by field; truncation may be silent
CLOBStringUTF-8Long text fieldsMay not be available via fields parameter
LOB/BLOBStringBase64-encodedBinary dataMust decode; check Content-Transfer-Encoding
Flexfield (DFF)ObjectNested JSONDescriptive flexfieldSegment names are configurable per tenant; never hardcode
LookupStringCode valueLookup code, not meaningAPI returns internal code; use expand=lookups for both

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

CodeMeaningCauseResolution
400Bad RequestMalformed JSON, invalid field name, wrong query syntaxCheck request body; verify field names via /describe; check framework version for query syntax
401UnauthorizedExpired token, invalid credentialsRe-authenticate; check token expiry; verify OAuth client configuration
403ForbiddenMissing role or data security policyAssign correct duty/job roles to integration user; check data security policies
404Not FoundWrong resource name, invalid record ID, wrong API versionVerify resource name at /{apiBase}/resources/{version}
429Too Many RequestsRate limit exceeded (~5,000 calls/hr/user)Implement exponential backoff; reduce call frequency; batch operations
500Internal Server ErrorOracle server-side issue, complex query timeoutSimplify query; reduce expand depth; retry after delay
503Service UnavailableMaintenance window, tenant restartWait and retry; check Oracle Cloud status page

Failure Points in Production

Anti-Patterns

Wrong: Trusting totalCount instead of hasMore for pagination

# BAD -- totalCount is capped at 499 when limit >= 500
response = requests.get(f"{url}?limit=500")
data = response.json()
total = data["totalResults"]  # Returns 499 (WRONG!)
# Developer thinks there are only 499 records

Correct: Use hasMore flag with limit=499

# GOOD -- loop on hasMore, always use limit=499
all_records = []
offset = 0
while True:
    response = requests.get(f"{url}?limit=499&offset={offset}")
    data = response.json()
    all_records.extend(data["items"])
    if not data.get("hasMore", False):
        break
    offset += 499

Wrong: Using REST API for bulk data extraction

# BAD -- extracting 500K records via REST API
# At 499 records per page = 1,000+ API calls
# At ~5,000 calls/hr limit, this takes hours
for offset in range(0, 500000, 499):
    resp = requests.get(f"{url}?limit=499&offset={offset}")
    records.extend(resp.json()["items"])

Correct: Use BICC for large extracts, REST for real-time

# GOOD -- use BICC for bulk extraction
# 1. Configure BICC extract in Oracle Fusion
# 2. Schedule incremental extract
# 3. Retrieve files from UCM
# Use REST only for real-time lookups on individual records
resp = requests.get(f"{url}/{record_id}?fields=Id,Status,Amount")

Wrong: New OAuth token for every API call

# BAD -- requests a new token for every call
def get_invoice(invoice_id):
    token = get_new_oauth_token()  # New token EVERY call!
    return requests.get(f"{url}/{invoice_id}",
                       headers={"Authorization": f"Bearer {token}"})

Correct: Cache and reuse tokens until near expiry

# GOOD -- cache token and reuse
class TokenManager:
    def get_token(self):
        if time.time() < self._expires_at - 60:  # 60s buffer
            return self._token
        resp = requests.post(self.token_url, auth=self.auth,
            data={"grant_type": "client_credentials"})
        data = resp.json()
        self._token = data["access_token"]
        self._expires_at = time.time() + data["expires_in"]
        return self._token

Common Pitfalls

Diagnostic Commands

# Test authentication (Basic Auth for quick testing)
curl -s -o /dev/null -w "%{http_code}" \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -u "USERNAME:PASSWORD"
# Expected: 200 (authenticated) or 401 (bad credentials)

# Discover available resources
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -20

# Get resource metadata (field names and types)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/describe" \
  -H "Authorization: Bearer $TOKEN"

# Test pagination -- verify hasMore behavior
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices?limit=2&totalResults=true" \
  -H "Authorization: Bearer $TOKEN"

# Check OAuth token validity
curl -s -o /dev/null -w "%{http_code}" \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -H "Authorization: Bearer $TOKEN"

Version History & Compatibility

Framework VersionAvailable SinceStatusKey ChangesMigration Notes
v424B (2024 Q2)CurrentLatest query enhancements, improved performanceRecommended for new integrations
v323D (2023 Q4)SupportedNested children as collections with hasMore/countBreaking: child resources change from arrays to collection objects
v222C (2022 Q3)Supportedrowmatch expressions; onlyData, orderBy, totalResultsNon-breaking; adds new query syntax
v1InitialSupportedBasic q=field=value; expand, fields, limit, offsetOriginal framework; still works but limited

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Real-time individual record CRUD (<500 records)Bulk data extraction (>10K records)BICC for large outbound extracts
Interactive lookups with field filtering and expandBulk data import (>500 records)FBDI for inbound bulk loads
Event-driven polling with LastUpdateDate filterComplex reporting/analytics queriesBIP (BI Publisher) reports
Lightweight validation lookupsFull data migration (initial load)FBDI + HCM Data Loader
Webhook alternative via pollingNeed guaranteed exactly-once deliveryOIC with Business Events subscription

Important Caveats

Related Units