---
# === IDENTITY ===
id: business/erp-integration/oracle-fusion-rest-api-deep-dive/2026
canonical_question: "What are the Oracle Fusion REST API capabilities - 499-record limit, pagination, expand, field filtering?"
aliases:
  - "Oracle Fusion Cloud REST API record limit and pagination"
  - "Oracle ERP Cloud REST API expand fields onlyData query parameters"
  - "Oracle Fusion Applications REST API framework versions and capabilities"
  - "Oracle Cloud ERP fscmRestApi endpoint structure and authentication"
entity_type: erp_integration
domain: business > erp-integration > oracle-fusion-rest-api-deep-dive
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === SYSTEM PROFILE ===
systems:
  - name: "Oracle Fusion Cloud Applications (ERP, SCM, HCM, CX)"
    vendor: "Oracle"
    version: "Release 25D (REST Framework v4)"
    edition: "Enterprise"
    deployment: "cloud"
    api_surface: "REST"

# === VERIFICATION ===
last_verified: 2026-03-09
confidence: 0.88
version: 1.0
first_published: 2026-03-09

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: volatile
  last_breaking_change: "REST Framework v4 (24B)"
  next_review: 2026-09-05
  change_sensitivity: high

# === CONSTRAINTS ===
constraints:
  - "Max 499 records per REST GET request (limit parameter capped at 499; requesting 500+ silently returns only 499 and sets hasMore=true)"
  - "Default page size is 25 records if no limit parameter is specified"
  - "POST operations recommended max 500 records per request for optimal performance"
  - "FBDI ZIP file max 250 MB; individual CSV files max 1 GB; max 100,000 records per single import"
  - "REST API not recommended for bulk data extraction (>10K records) -- use BICC or BIP instead"
  - "Rate limiting: approximately 5,000 API calls per hour per user (enforced per tenant, returns 429 on breach)"
  - "SOAP API deprecated for new development -- REST is the strategic direction"
  - "Framework version affects payload structure -- v3+ returns nested children as collections, not arrays"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "User needs bulk data extraction (>100K records outbound)"
    use_instead: "business/erp-integration/oracle-bicc-data-extraction/2026"
  - condition: "User needs Oracle NetSuite API capabilities"
    use_instead: "the NetSuite SuiteTalk REST API for record-level CRUD"
  - condition: "User needs Oracle Integration Cloud (OIC) capabilities"
    use_instead: "business/erp-integration/oracle-integration-cloud-capabilities/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: integration_pattern
    question: "What integration pattern do you need?"
    type: choice
    options:
      - "real-time sync (individual records, <1s latency)"
      - "batch/bulk (scheduled, high volume)"
      - "event-driven (Business Events, CDC)"
      - "file-based (FBDI CSV/XML import)"
  - key: data_volume
    question: "What's your daily data volume?"
    type: choice
    options:
      - "< 1,000 records/day"
      - "1,000-100,000 records/day"
      - "> 100,000 records/day"
  - key: direction
    question: "What's the data flow direction?"
    type: choice
    options:
      - "inbound (writing to Oracle Fusion)"
      - "outbound (reading from Oracle Fusion)"
      - "bidirectional sync"

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/business/erp-integration/oracle-fusion-rest-api-deep-dive/2026"
suggested_citation: "Source: knowledgelib.io -- AI Knowledge Library (verified 2026-03-09)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: "business/erp-integration/oracle-integration-cloud-capabilities/2026"
      label: "Oracle Integration Cloud (OIC) iPaaS capabilities"
  solves: []
  often_confused_with:
    - id: "business/erp-integration/oracle-bicc-data-extraction/2026"
      label: "Oracle BICC bulk extraction (different API surface for large extracts)"

# === SOURCES ===
sources:
  - id: src1
    title: "REST API for Common Features in Oracle Fusion Cloud Applications"
    author: Oracle
    url: https://docs.oracle.com/en/cloud/saas/applications-common/24d/farca/Resource_Methods.html
    type: official_docs
    published: 2024-10-01
    reliability: authoritative
  - id: src2
    title: "What is Max supported value for limit parameter in GET REST API Call?"
    author: Oracle Support
    url: https://support.oracle.com/knowledge/Oracle%20Fusion%20Applications/2429019_1.html
    type: official_docs
    published: 2023-01-15
    reliability: authoritative
  - id: src3
    title: "How to Extract Large Data from Oracle Fusion Using REST APIs in OIC"
    author: SOAlicious (Community)
    url: https://soalicious.blogspot.com/2024/06/oic-how-to-extract-large-data-from.html
    type: community_resource
    published: 2024-06-15
    reliability: moderate_high
  - id: src4
    title: "Authenticating and Working with Oracle Fusion Applications V1 REST APIs"
    author: Oracle A-Team
    url: https://www.ateam-oracle.com/authenticate-and-work-with-fusion-v1-api-rest
    type: technical_blog
    published: 2023-06-01
    reliability: high
  - id: src5
    title: "Data Import Options and Guidelines for Fusion Applications Suite"
    author: Oracle A-Team
    url: https://www.ateam-oracle.com/data-import-options-and-guidelines-for-fusion-applications-suite
    type: technical_blog
    published: 2024-01-15
    reliability: high
  - id: src6
    title: "Oracle Fusion Cloud ERP Integrations Guidelines and Patterns Using Oracle PaaS"
    author: Oracle A-Team
    url: https://www.ateam-oracle.com/oracle-fusion-cloud-erp-applications-integrations-guidelines-patternsuse-cases-using-oracle-paas
    type: technical_blog
    published: 2024-03-01
    reliability: high
  - id: src7
    title: "Oracle Cloud ERP REST API Guide and Best Practices"
    author: Broadway InfoSys
    url: https://ftp.broadwayinfosys.com/blog/oracle-cloud-erp-rest-api-guide-and-best-practices-1764798113
    type: technical_blog
    published: 2025-06-01
    reliability: moderate_high
---

# Oracle Fusion Cloud REST API Deep Dive: 499-Record Limit, Pagination, Expand & Field Filtering

## TL;DR

- **Bottom line**: Oracle Fusion REST API caps every GET response at 499 records (not 500) -- you must implement offset-based pagination loops checking `hasMore` to retrieve complete datasets. Use REST for real-time CRUD under 10K records; switch to FBDI for bulk imports and BICC for bulk extracts. [src2]
- **Key limit**: 499 records per GET request (hard cap); ~5,000 API calls/hour/user; POST operations recommended max 500 records per batch. Setting limit > 499 silently returns 499 and reports totalCount as 499, which misleads pagination logic. [src2, src3]
- **Watch out for**: Setting `limit=500` does NOT return 500 records -- it returns 499 AND reports `totalCount` as 499, hiding the true total count. Always use `limit=499` and rely on the `hasMore` flag instead of `totalCount`. [src2, src3]
- **Best for**: Real-time individual record CRUD, moderate-volume queries (<10K records), interactive lookups with field filtering and expand for child resources. [src6]
- **Authentication**: Basic Auth over SSL (simplest), OAuth 2.0 via Oracle IAM/IDCS (recommended for production), SAML 2.0 bearer token, or JWT token in Authorization header. [src4]

## 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 (BC) framework and has evolved through multiple framework versions (v1 through v4), each adding query and response capabilities. This card covers the REST API surface specifically -- not SOAP (deprecated for new development), not BICC (bulk extraction), and not FBDI (file-based import), though it references when to use each. All Oracle Fusion Cloud editions share the same REST API capabilities; there are no edition-specific API restrictions. The API is cloud-only -- Oracle E-Business Suite and PeopleSoft use completely different API surfaces.

| Property | Value |
|---|---|
| **Vendor** | Oracle |
| **System** | Oracle Fusion Cloud Applications (ERP, SCM, HCM, CX) |
| **API Surface** | REST (JSON over HTTPS) |
| **Current Framework Version** | v4 (available since 24B release) |
| **Editions Covered** | All editions (no API differences by edition) |
| **Deployment** | Cloud (SaaS only -- no on-premise option for Fusion) |
| **API Docs URL** | [Oracle Fusion Cloud REST API Reference](https://docs.oracle.com/en/cloud/saas/applications-common/24d/farca/index.html) |
| **Status** | GA (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 Surface | Protocol | Best For | Max Records/Request | Rate Limit | Real-time? | Bulk? |
|---|---|---|---|---|---|---|
| REST API (fscmRestApi) | HTTPS/JSON | Real-time CRUD, <10K records | 499 per GET page | ~5,000 calls/hr/user | Yes | No |
| SOAP Web Services | HTTPS/XML | Legacy integrations, metadata ops | Varies by service | Shared with REST | Yes | No |
| FBDI (File-Based Data Import) | CSV in ZIP via REST | Bulk inbound imports, data migration | 100,000 per import job | 5 concurrent imports | No | Yes |
| BICC (BI Cloud Connector) | Bulk extract via UCM | Large outbound data extraction | Millions (full/incremental) | Scheduled | No | Yes |
| BIP (BI Publisher) | SOAP/REST | Report-based data extraction | Report-dependent | Report queue limits | No | Partial |
| Business Events | Event subscription | Event-driven integration via OIC | N/A (push) | Event queue limits | Yes | N/A |

## Rate Limits & Quotas

### Per-Request Limits

| Limit Type | Value | Applies To | Notes |
|---|---|---|---|
| Max records per GET response | 499 | REST API GET (all resources) | Hard cap. Setting limit > 499 silently returns 499 and misreports totalCount [src2] |
| Default page size | 25 | REST API GET (no limit param) | Override with `?limit=499` for efficiency [src1] |
| Max POST records (recommended) | 500 | REST API POST/batch creates | Performance degrades significantly above this threshold [src5] |
| Max request payload size | ~10 MB | REST API POST/PATCH | Larger payloads may timeout or be rejected by WAF |
| Max FBDI ZIP file size | 250 MB | File-Based Data Import | Individual CSV files within ZIP can be up to 1 GB [src5] |
| Max FBDI records per import | 100,000 | File-Based Data Import | Split larger datasets into multiple import jobs [src5] |
| Max concurrent FBDI imports | 5 | File-Based Data Import | Additional imports queue behind running jobs [src5] |

[src1, src2, src5]

### Rolling / Daily Limits

| Limit Type | Value | Window | Edition Differences |
|---|---|---|---|
| API calls per user | ~5,000 | Per hour (rolling) | No edition differences -- same for all Fusion Cloud tenants |
| Total tenant API calls | Fair-use throttling | Rolling window | Oracle reserves right to throttle tenants impacting shared infrastructure |
| FBDI concurrent imports | 5 | Per tenant | Shared across all users and integration flows [src5] |
| BIP concurrent reports | Queue-based | Per tenant | Long-running reports can block the queue |
| BICC extract jobs | Scheduled | Per tenant | Typically 1-2 concurrent extract streams |

[src2, src6]

## Authentication

Oracle Fusion Cloud REST APIs use a global Oracle Web Services Manager (OWSM) security policy called Multi Token Over SSL. Multiple authentication methods are supported. [src4]

| Flow | Use When | Token Lifetime | Refresh? | Notes |
|---|---|---|---|---|
| Basic Auth over SSL | Testing, simple integrations | Per request | No | Username:password in base64; simple but not recommended for production [src4] |
| OAuth 2.0 (Client Credentials) | Server-to-server production integrations | Configurable (default ~1h) | Yes | Recommended; requires Oracle IAM confidential app setup [src4] |
| OAuth 2.0 (JWT Bearer) | Automated integrations, service accounts | JWT-defined | New JWT per request | Requires certificate registration in Oracle IAM [src4] |
| SAML 2.0 Bearer | SSO federation, cross-cloud identity | Assertion validity | No | Token passed in HTTP Authorization header [src4] |
| OAuth 2.0 (3-legged) | User-context operations, delegated access | Access: ~1h, Refresh: until revoked | Yes | Standard authorization code flow; requires callback URL |

[src4]

### Authentication Gotchas

- Basic Auth sends credentials with every request and does not support MFA. Oracle recommends OAuth 2.0 for all production integrations. [src4]
- OAuth 2.0 token endpoint URL varies by Oracle IAM domain: `https://{idcs-domain}.identity.oraclecloud.com/oauth2/v1/token`. Do not hardcode -- it changes between environments. [src4]
- JWT bearer flow requires registering the signing certificate in Oracle IAM as a trusted partner. Self-signed certificates work for development; use CA-signed for production. [src4]
- The integration user's role and data security policies determine API access scope. A 403 Forbidden error usually means missing role assignment, not wrong credentials.
- Session timeout can be configured by the Oracle Cloud administrator -- do not hardcode token lifetimes.

## Constraints
<!-- Agents: read this section before recommending any integration approach from this unit.
     These are hard constraints that must not be violated. -->

- **499-record hard cap**: Every REST GET request returns a maximum of 499 records regardless of the `limit` parameter value. Setting `limit=500` or higher silently returns 499 and incorrectly reports `totalCount` as 499. [src2]
- **REST is not for bulk operations**: Oracle explicitly recommends against using REST API for high-volume data extraction (>10K records) or bulk imports. Use BICC for outbound bulk and FBDI for inbound bulk. [src5, src6]
- **Framework version affects response structure**: REST framework v3+ changes nested child resources from arrays to collection objects with `hasMore`/`count` metadata. Integration code written for v1/v2 responses will break on v3+ payloads. [src1]
- **No webhooks/push notifications via REST**: REST API is purely request/response. For event-driven patterns, you must use Business Events via Oracle Integration Cloud (OIC) or poll with `LastUpdateDate` filters. [src6]
- **SOAP deprecated for new development**: Oracle has stated SOAP services should not be used when an equivalent REST API exists. However, some older business objects still only have SOAP interfaces. [src6]
- **Multi-tenant fair-use throttling**: Oracle can throttle individual tenants that generate excessive API load, even below the per-user limit, to protect shared infrastructure.
- **No direct database access**: Unlike on-premise ERP systems, Oracle Fusion Cloud provides no direct SQL access. All data access must go through REST, BICC, BIP, or FBDI interfaces.

## 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 (expect 2-20 API calls)
|   |   |   +-- 10,000 - 100,000 records --> REST is slow; consider BIP reports or BICC incremental
|   |   |   +-- > 100,000 records --> BICC (mandatory for large extracts) [src6]
|   |   +-- Need real-time or scheduled?
|   |       +-- Real-time --> REST API with LastUpdateDate filter for delta
|   |       +-- Scheduled --> BICC incremental extract (most efficient)
|   +-- INBOUND (writing to Fusion)?
|   |   +-- How many records per load?
|   |   |   +-- < 500 records --> REST API POST/PATCH (one at a time or small batches)
|   |   |   +-- 500 - 100,000 records --> FBDI (file-based import via REST upload) [src5]
|   |   |   +-- > 100,000 records --> FBDI with split files (multiple 100K batches) [src5]
|   |   +-- 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 [src6]
|       |   +-- NO --> REST API polling with LastUpdateDate filter (fallback)
|       +-- Need guaranteed delivery?
|           +-- YES --> OIC + dead letter queue
|           +-- NO --> REST polling with retry
+-- Which REST framework version?
|   +-- v1: Simple array responses, basic query syntax (q=field=value)
|   +-- v2+: rowmatch expressions, improved query filtering [src1]
|   +-- v3+: Nested children as collections (with hasMore/count), fields parameter pagination [src1]
|   +-- v4: Latest, most complete feature set (24B+)
+-- Error tolerance?
    +-- Zero-loss required --> Idempotent design + FBDI (has built-in error reporting per record)
    +-- Best-effort --> REST with retry on 429/5xx
```

## Quick Reference

### REST API Endpoint Reference

| Operation | Method | Endpoint Pattern | Payload | Notes |
|---|---|---|---|---|
| List resources | GET | `/{apiBase}/resources/{version}/{resource}` | N/A | Returns max 499 records; use offset for pagination [src1] |
| Get single record | GET | `/{apiBase}/resources/{version}/{resource}/{id}` | N/A | Returns full record with expand support [src1] |
| Create record | POST | `/{apiBase}/resources/{version}/{resource}` | JSON | Returns created record with generated ID |
| Update record | PATCH | `/{apiBase}/resources/{version}/{resource}/{id}` | JSON | Partial update; only send changed fields |
| Replace record | PUT | `/{apiBase}/resources/{version}/{resource}/{id}` | JSON | Full replacement; all fields required |
| Delete record | DELETE | `/{apiBase}/resources/{version}/{resource}/{id}` | N/A | Permanent; no soft-delete via API |
| Query with filter | GET | `/{apiBase}/resources/{version}/{resource}?q=expr` | N/A | v1: `q=field=value`; v2+: rowmatch [src1] |
| Expand children | GET | `/{apiBase}/resources/{version}/{resource}?expand={child}` | N/A | Fetches child resources inline [src1] |
| Select fields | GET | `/{apiBase}/resources/{version}/{resource}?fields={f1,f2}` | N/A | Reduces payload size; improves performance [src1] |
| Metadata only | GET | `/{apiBase}/resources/{version}/{resource}?onlyData=false` | N/A | Returns resource metadata without data rows [src1] |

### Common API Base URLs

| Pillar | API Base Path | Example |
|---|---|---|
| Financials/SCM | `/fscmRestApi` | `https://{host}/fscmRestApi/resources/11.13.18.05/invoices` |
| HCM | `/hcmRestApi` | `https://{host}/hcmRestApi/resources/11.13.18.05/workers` |
| CRM/CX | `/crmRestApi` | `https://{host}/crmRestApi/resources/11.13.18.05/accounts` |
| Common | `/commonRestApi` | `https://{host}/commonRestApi/resources/11.13.18.05/users` |

[src1, src4]

### Key Query Parameters

| Parameter | Purpose | Example | Framework Version |
|---|---|---|---|
| `limit` | Records per page (max 499) | `?limit=499` | All |
| `offset` | Skip N records for pagination | `?offset=499` | All |
| `q` | Filter expression | `?q=Status=APPROVED` (v1) or `?q=Status eq 'APPROVED'` (v2+) | All (syntax varies) |
| `fields` | Select specific fields | `?fields=InvoiceId,InvoiceNumber,Amount` | All |
| `expand` | Include child resources | `?expand=invoiceLines` | All |
| `onlyData` | Exclude metadata wrapper | `?onlyData=true` | v2+ |
| `orderBy` | Sort results | `?orderBy=CreationDate:desc` | v2+ |
| `totalResults` | Include total count in response | `?totalResults=true` | v2+ |
| `finder` | Use predefined query finder | `?finder=FindByStatus;Status=OPEN` | All |

[src1]

## 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. Basic Auth works for testing but is not recommended for production. [src4]

```bash
# 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"

# Response:
# {"access_token":"eyJ...","token_type":"Bearer","expires_in":3600}
```

**Verify**: `echo $ACCESS_TOKEN | cut -d. -f2 | base64 -d 2>/dev/null | jq .exp` --> expected: Unix timestamp ~3600 seconds from now

### 2. Query a resource with pagination

Fetch records using the 499-record pagination pattern. Always use `hasMore` to control the loop, never rely on `totalCount`. [src2, src3]

```bash
# 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,InvoiceDate&q=InvoiceStatus=APPROVED" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json"

# Response structure:
# {
#   "items": [...],     <-- array of records (max 499)
#   "count": 499,       <-- records in this page
#   "hasMore": true,    <-- MORE RECORDS EXIST
#   "limit": 499,
#   "offset": 0,
#   "totalResults": 1247  <-- only reliable with totalResults=true param
# }
```

**Verify**: Check `count` and `hasMore` fields in response --> expected: `count <= 499`, `hasMore` is boolean

### 3. Implement full pagination loop

Loop through all pages using offset increments of your page size until `hasMore` is false. [src3]

```python
import requests

BASE_URL = "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices"
HEADERS = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

all_records = []
offset = 0
page_size = 499  # Max allowed value

while True:
    params = {
        "limit": page_size,
        "offset": offset,
        "fields": "InvoiceId,InvoiceNumber,InvoiceAmount",
        "q": "InvoiceStatus=APPROVED",
        "onlyData": "true"
    }
    resp = requests.get(BASE_URL, headers=HEADERS, params=params, timeout=60)
    resp.raise_for_status()
    data = resp.json()

    all_records.extend(data.get("items", []))

    if not data.get("hasMore", False):
        break  # No more pages

    offset += page_size

print(f"Total records retrieved: {len(all_records)}")
```

**Verify**: `len(all_records)` --> expected: matches the total record count in Oracle Fusion

### 4. Use expand and field filtering for efficiency

Reduce payload size and network round trips by selecting only needed fields and expanding child resources inline. [src1]

```bash
# Fetch invoices with only key fields AND expand invoice lines
curl -s -X GET \
  "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/12345?fields=InvoiceId,InvoiceNumber,InvoiceAmount&expand=invoiceLines(fields=LineNumber,LineAmount,Description)" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# The expand parameter fetches child resources in a single request
# instead of requiring separate API calls per parent record.
# In framework v3+, expanded children include hasMore/count metadata
# for their own pagination.
```

**Verify**: Response includes `invoiceLines` nested under the invoice --> expected: array (v1/v2) or collection with `hasMore` (v3+)

### 5. Create a record via POST

Insert a new business object using REST API. [src7]

```bash
# 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",
    "SupplierName": "Acme Corp",
    "InvoiceDate": "2026-03-09",
    "BusinessUnit": "US Operations"
  }'

# Success response: HTTP 201 Created
# Returns the created record with system-generated fields (InvoiceId, etc.)
```

**Verify**: HTTP status 201 --> expected: response body contains `InvoiceId` with a system-generated value

### 6. Handle rate limiting and errors

Implement exponential backoff for 429 (rate limit) and 5xx (server error) responses. [src7]

```python
import requests
import time

def call_fusion_api(url, headers, params=None, max_retries=5):
    """Call Oracle Fusion REST API with retry logic."""
    for attempt in range(max_retries):
        try:
            resp = requests.get(url, headers=headers, params=params, timeout=60)

            if resp.status_code == 200:
                return resp.json()
            elif resp.status_code == 429:
                # Rate limited -- back off exponentially
                wait = min(2 ** attempt * 2, 120)  # Max 2 minutes
                print(f"Rate limited (429). Waiting {wait}s...")
                time.sleep(wait)
                continue
            elif resp.status_code >= 500:
                # Server error -- retry with backoff
                wait = min(2 ** attempt * 3, 180)
                print(f"Server error ({resp.status_code}). Waiting {wait}s...")
                time.sleep(wait)
                continue
            elif resp.status_code == 403:
                raise PermissionError(
                    f"403 Forbidden: Check integration user roles and data security policies"
                )
            elif resp.status_code == 404:
                raise ValueError(
                    f"404 Not Found: Verify resource name and API version in URL"
                )
            else:
                resp.raise_for_status()
        except requests.exceptions.Timeout:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            raise

    raise Exception(f"Failed after {max_retries} retries")
```

**Verify**: Test with an invalid endpoint --> expected: `ValueError` with 404 message; test with invalid auth --> expected: 401 response

## Code Examples

### Python: Paginated extraction of all approved invoices

```python
# Input:  Oracle Fusion Cloud credentials, business unit filter
# Output: Complete list of approved invoices as Python dicts

import requests
import time

class OracleFusionClient:
    def __init__(self, host, client_id, client_secret, idcs_domain):
        self.host = host
        self.client_id = client_id
        self.client_secret = client_secret
        self.idcs_domain = idcs_domain
        self.access_token = None

    def authenticate(self):
        """Obtain OAuth 2.0 access token."""
        resp = requests.post(
            f"https://{self.idcs_domain}.identity.oraclecloud.com/oauth2/v1/token",
            auth=(self.client_id, self.client_secret),
            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):
        """Paginate through all records for a resource."""
        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)  # Back off on rate limit
                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

# Usage
client = OracleFusionClient(
    host="mycompany.fa.us2.oraclecloud.com",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    idcs_domain="idcs-abc123"
)
client.authenticate()

invoices = client.get_all_records(
    resource="invoices",
    query="InvoiceStatus=APPROVED",
    fields="InvoiceId,InvoiceNumber,InvoiceAmount,InvoiceDate"
)
print(f"Retrieved {len(invoices)} approved invoices")
```

### JavaScript/Node.js: Fetch with expand and field filtering

```javascript
// 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) {
  // Build URL with expand and fields parameters
  // Max 499 records for child collections in framework v3+
  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}`,
        'Content-Type': 'application/json'
      }
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        if (res.statusCode === 200) {
          resolve(JSON.parse(data));
        } else if (res.statusCode === 429) {
          reject(new Error('Rate limited - implement backoff'));
        } else {
          reject(new Error(`HTTP ${res.statusCode}: ${data}`));
        }
      });
    });
    req.on('error', reject);
    req.setTimeout(60000, () => { req.destroy(); reject(new Error('Timeout')); });
  });
}

// Usage: Fetch purchase order with expanded lines
// fetchWithExpand(host, token, 'purchaseOrders', '300000012345678',
//   'lines(fields=LineNumber,ItemDescription,Quantity,UnitPrice)',
//   'OrderNumber,OrderedAmount,Supplier');
```

### cURL: Common diagnostic queries

```bash
# Input:  Access token, Oracle Fusion Cloud host
# Output: JSON responses for common API operations

# 1. List available resources (discover API endpoints)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -50

# 2. Get resource metadata (field names, types, relationships)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/describe" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

# 3. Query with server-side filter (reduce payload)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices?\
limit=5&fields=InvoiceId,InvoiceNumber,InvoiceAmount&\
q=InvoiceDate%3E2026-01-01&orderBy=InvoiceDate:desc" \
  -H "Authorization: Bearer $TOKEN"

# 4. Check if more records exist (pagination test)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices?\
limit=1&totalResults=true" \
  -H "Authorization: Bearer $TOKEN" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Total: {d.get(\"totalResults\",\"N/A\")}, HasMore: {d.get(\"hasMore\")}')"
```

## Data Mapping

### REST API Field Type Reference

| Oracle Fusion Type | JSON Type | API Format | Transform Notes | Gotcha |
|---|---|---|---|---|
| DATE | String | `YYYY-MM-DD` | No timezone info | Oracle stores as UTC internally; display depends on user timezone preference |
| DATETIME/TIMESTAMP | String | `YYYY-MM-DDTHH:MM:SS+00:00` | ISO 8601 with timezone | Always include timezone offset to avoid ambiguity |
| NUMBER | Number | Decimal | No rounding | Currency amounts may have 2-6 decimal places depending on currency |
| VARCHAR2 | String | UTF-8 | Direct mapping | Max length varies by field; truncation is silent on some objects |
| CLOB | String | UTF-8 | Long text fields | May not be available via `fields` parameter; requires full record fetch |
| LOB/BLOB | String | Base64-encoded | Binary data | Must decode; check Content-Transfer-Encoding header |
| Flexfield (DFF) | Object | Nested JSON | Descriptive flexfield | Segment names are configurable per tenant; never hardcode segment names |
| Lookup | String | Code value | Lookup code, not meaning | API returns internal code (e.g., "APPROVED"); UI shows meaning (e.g., "Approved"). Use `expand=lookups` for both |

### Data Type Gotchas

- Oracle Fusion REST API returns dates as strings in `YYYY-MM-DD` format with no timezone. The actual timezone depends on the user profile of the integration user -- always configure the integration user with UTC timezone. [src7]
- Flexfield (DFF) segment names are configurable and differ between tenants. An integration built for one customer's DFF configuration will break on another. Always use the `/describe` endpoint to discover segment names dynamically. [src1]
- Amount fields do not include currency -- you must fetch the currency field separately. Multi-currency transactions require explicit currency code + amount pairs.
- LOV (List of Values) fields return internal codes via API but display human-readable meanings in the UI. Use the `finder` parameter or `expand=lookups` to get both code and meaning. [src1]

## Error Handling & Failure Points

### Common Error Codes

| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 400 | Bad Request | Malformed JSON, invalid field name, wrong query syntax | Check request body structure; verify field names via `/describe` endpoint; check framework version for query syntax [src1] |
| 401 | Unauthorized | Expired token, invalid credentials | Re-authenticate; check token expiry; verify OAuth client configuration |
| 403 | Forbidden | Missing role or data security policy | Assign correct duty/job roles to integration user; check data security policies for business unit access |
| 404 | Not Found | Wrong resource name, invalid record ID, wrong API version | Verify resource name at `/{apiBase}/resources/{version}`; check API version string (e.g., 11.13.18.05) [src1] |
| 429 | Too Many Requests | Rate limit exceeded (~5,000 calls/hr/user) | Implement exponential backoff; reduce call frequency; batch operations where possible |
| 500 | Internal Server Error | Oracle server-side issue, complex query timeout | Simplify query; reduce expand depth; retry after delay; contact Oracle Support if persistent |
| 503 | Service Unavailable | Maintenance window, tenant restart, infrastructure issue | Wait and retry; check Oracle Cloud status page; schedule integrations around maintenance windows |

### Failure Points in Production

- **Silent 499 truncation**: Setting `limit=500` returns exactly 499 records AND reports `totalCount=499`, completely hiding that more records exist. Integrations that trust `totalCount` silently lose data. Fix: `Always use limit=499 and loop on hasMore=true; never trust totalCount as the authoritative record count`. [src2, src3]
- **Framework version mismatch**: Code expecting v1/v2 array responses breaks on v3+ collection responses. Nested children change from `"lines": [...]` to `"lines": {"items": [...], "hasMore": false}`. Fix: `Check the framework version in the API URL and parse responses accordingly; test against the actual API version your tenant uses`. [src1]
- **Integration user timezone**: Date filters in queries use the integration user's timezone preference, not UTC. An integration user configured for EST and another for UTC will return different records for the same date filter. Fix: `Set all integration users to UTC timezone in Oracle Fusion user preferences`. [src7]
- **Flexfield drift**: DFF segments are configured by functional administrators and can change without notice to the integration team. Fix: `Use the /describe endpoint to validate flexfield segments before processing; build segment name mapping as configuration, not code`.
- **OAuth token caching failure**: Not caching the OAuth token and requesting a new one per API call quickly exhausts the rate limit. Fix: `Cache the access token and reuse until near expiry (subtract 60s buffer); implement a token manager that handles refresh`. [src4]
- **FBDI silent row failures**: FBDI import jobs can succeed at the job level while individual rows fail validation. The job status shows "Succeeded" but records were skipped. Fix: `Always check the FBDI import error report (BIP output) after each job; implement row-level error handling in your orchestration`. [src5]

## Anti-Patterns

### Wrong: Trusting totalCount instead of hasMore for pagination

```python
# BAD -- totalCount is capped at 499 when limit >= 500
# This code will only ever retrieve 499 records even if 10,000 exist
response = requests.get(f"{url}?limit=500")
data = response.json()
total = data["totalResults"]  # Returns 499 (WRONG!)
# Developer thinks there are only 499 records and stops paginating
```

### Correct: Use hasMore flag with limit=499

```python
# 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
# Now all_records contains the complete dataset
```

### Wrong: Using REST API for bulk data extraction

```python
# BAD -- extracting 500K GL journal entries via REST API
# At 499 records per page, this requires 1,000+ API calls
# At ~5,000 calls/hr limit, this takes hours and may be throttled
for offset in range(0, 500000, 499):
    resp = requests.get(f"{url}?limit=499&offset={offset}")
    records.extend(resp.json()["items"])
    # This will hit rate limits, timeout, and is extremely inefficient
```

### Correct: Use BICC for large extracts, REST for real-time

```python
# GOOD -- use BICC for bulk extraction
# 1. Configure BICC extract in Oracle Fusion for GL Journals
# 2. Schedule incremental extract (captures only changed records)
# 3. Retrieve extracted files from UCM (Universal Content Manager)
# 4. Process extracted CSV/Parquet files locally

# Use REST only for real-time lookups on individual records
resp = requests.get(f"{url}/{journal_id}?fields=JournalId,Status,Amount")
```

### Wrong: Making a new OAuth token request for every API call

```python
# BAD -- requests a new token for every single API call
# Wastes rate limit budget on token requests
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

```python
# GOOD -- cache token and reuse
import time

class TokenManager:
    def __init__(self, client_id, client_secret, token_url):
        self.client_id = client_id
        self.client_secret = client_secret
        self.token_url = token_url
        self._token = None
        self._expires_at = 0

    def get_token(self):
        if time.time() < self._expires_at - 60:  # 60s buffer
            return self._token
        resp = requests.post(self.token_url,
            auth=(self.client_id, self.client_secret),
            data={"grant_type": "client_credentials",
                  "scope": "urn:opc:resource:consumer::all"})
        data = resp.json()
        self._token = data["access_token"]
        self._expires_at = time.time() + data["expires_in"]
        return self._token
```

## Common Pitfalls

- **Setting limit=500 instead of 499**: The max is 499, not 500. Setting 500 not only returns 499 records but also misreports the total count, silently corrupting pagination logic. Always use `limit=499`. [src2]
- **Not using the fields parameter**: Without `fields`, the API returns ALL attributes for every record, including LOB fields and flexfields. This dramatically increases response size and slows pagination. Always specify only the fields you need. [src1]
- **Ignoring framework version differences**: An integration built against framework v1 response format will break when Oracle upgrades to v3+ (children change from arrays to collection objects). Pin your API URL version and handle both formats. [src1]
- **Assuming SOAP and REST have the same objects**: Some business objects are only available via SOAP, and some only via REST. The Suppliers SOAP service has different field names than the REST suppliers resource. Always verify availability via the REST `/describe` endpoint before assuming REST coverage. [src6]
- **Not setting integration user timezone to UTC**: Date-based queries (`q=LastUpdateDate>2026-01-01`) use the integration user's timezone, not the system timezone. Two integration users with different timezone settings querying the same date range get different results. [src7]
- **Running FBDI imports during peak hours**: FBDI jobs compete for the same processing resources as online users. Large imports during business hours cause UI slowdowns. Schedule FBDI imports during off-peak windows. [src5]
- **Not checking FBDI error reports**: FBDI import jobs report status at the job level. Individual row failures are only visible in the BIP error report generated after import. A "Succeeded" job may have silently skipped thousands of invalid rows. [src5]

## Diagnostic Commands

```bash
# 1. 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)

# 2. Discover available resources
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05" \
  -H "Authorization: Bearer $TOKEN" \
  | python3 -m json.tool | grep '"rel"' | head -20

# 3. Get resource metadata (field names and types)
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/invoices/describe" \
  -H "Authorization: Bearer $TOKEN" \
  | python3 -c "import sys,json; [print(f'{a[\"name\"]:40s} {a[\"type\"]}') for a in json.load(sys.stdin).get('attributes',[])]"

# 4. 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" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'Count: {d.get(\"count\")}, HasMore: {d.get(\"hasMore\")}, TotalResults: {d.get(\"totalResults\",\"N/A\")}')"

# 5. 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"
# Expected: 200 (valid) or 401 (expired/invalid)

# 6. Test expand parameter with child resource
curl -s "https://YOUR_HOST/fscmRestApi/resources/11.13.18.05/purchaseOrders/300000012345678?\
expand=lines&fields=OrderNumber,Supplier&onlyData=true" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool | head -30
```

## Version History & Compatibility

| Framework Version | Available Since | Status | Key Changes | Migration Notes |
|---|---|---|---|---|
| v4 | 24B (2024 Q2) | Current | Latest query enhancements, improved performance | Recommended for new integrations |
| v3 | 23D (2023 Q4) | Supported | Nested children as collections (with `hasMore`/`count`); `fields` parameter paginates children | Breaking: child resources change from arrays to collection objects [src1] |
| v2 | 22C (2022 Q3) | Supported | `rowmatch` query expressions; `onlyData`, `orderBy`, `totalResults` parameters | Non-breaking; adds new query syntax alongside v1 |
| v1 | Initial | Supported | Basic `q=field=value` query; `expand`, `fields`, `limit`, `offset` | Original framework; still works but limited query power |

[src1]

### Deprecation Policy

Oracle Fusion Cloud follows a quarterly release cycle (A, B, C, D per year). REST API framework versions are cumulative -- newer versions add features but do not remove previous functionality. The API URL version string (e.g., `11.13.18.05`) maps to a specific release. Oracle does not formally deprecate REST framework versions but recommends using the latest. SOAP services are deprecated for new development where REST equivalents exist. Always verify your tenant's available API version using the resource catalog endpoint. [src1, src6]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Real-time individual record CRUD (<500 records per operation) | Bulk data extraction (>10K records) | BICC for large outbound extracts [src6] |
| Interactive lookups with field filtering and expand | Bulk data import (>500 records) | FBDI for inbound bulk loads [src5] |
| Event-driven polling with LastUpdateDate filter | Complex reporting/analytics queries | BIP (BI Publisher) reports for analytics [src6] |
| Lightweight validation lookups (check existence, get status) | Full data migration (initial load) | FBDI + HCM Data Loader for migration [src5] |
| Webhooks are needed but Business Events unavailable | Need guaranteed exactly-once delivery | OIC with Business Events subscription [src6] |

## Important Caveats

- The 499-record limit is a hard platform constraint that cannot be increased by Oracle Support or by any configuration change. It applies uniformly to all REST resources across ERP, SCM, HCM, and CX pillars. [src2]
- Rate limits (~5,000 calls/hr/user) are approximate and may vary. Oracle reserves the right to adjust throttling based on tenant impact to shared infrastructure. There is no published SLA on API call rates.
- REST API availability and resource coverage varies by Oracle Fusion Cloud release. New business objects are added and existing ones enhanced each quarterly release. Always check the API catalog for your specific release version.
- Framework version behavior is controlled by the API URL version string, not by a global tenant setting. Different integrations on the same tenant can use different framework versions simultaneously.
- Oracle Fusion Cloud is SaaS-only with no on-premise deployment option. This means all API traffic traverses the public internet (or Oracle Cloud Infrastructure FastConnect for private connectivity). Plan for network latency in integration design.

## Related Units

- [Oracle Integration Cloud (OIC) Capabilities](/business/erp-integration/oracle-integration-cloud-capabilities/2026) -- Oracle's middleware for Fusion Cloud integrations
- [Oracle NetSuite SuiteTalk REST API](/business/erp-integration/netsuite-suitetalk-rest-api/2026) -- different Oracle ERP with different API surface
- [Oracle BICC Data Extraction](/business/erp-integration/oracle-bicc-data-extraction/2026) -- bulk extraction alternative to REST API
