Oracle REST API Pagination Pitfalls — hasMore, totalResults, Offset Degradation

Type: ERP Integration System: Oracle Fusion Cloud (ERP, HCM, CX) Confidence: 0.86 Sources: 6 Verified: 2026-03-09 Freshness: 2026-03-09

TL;DR

System Profile

Oracle Fusion Cloud REST APIs (fscmRestApi, hcmRestApi, crmRestApi) use offset/limit pagination. The response includes hasMore, totalResults, count, offset, and limit — but several documented issues make naive implementations unreliable.

PropertyValue
VendorOracle
SystemOracle Fusion Cloud (ERP, HCM, CX)
Pagination TypeOffset/Limit only (no cursor)
Max Records/Page500 (some: 499)
Default Page Size25
DocsOracle REST API Reference

API Surfaces & Capabilities

FeatureExpectedActualReliable?
limitSet page sizeCapped at 500 silentlyWithin cap
offsetSkip N recordsWorks but degradesAt scale: No
hasMoretrue if more existCan be wrong with offsetNo
totalResultsTotal matchingCaps at page size with offsetNo
countCurrent page countAccurateYes
links.nextNext page URLUsually availableYes

Rate Limits & Quotas

Performance by Offset Range

Offset RangeResponse TimeReliabilityRecommendation
0-2,0001-3 secondsHighStandard pagination
2,000-10,0003-10 secondsMediumFilter-based chunking
10,000-50,00010-30 secondsLowDate-range partitioning
> 50,00030+ sec or timeoutVery lowUse FBDI or BIP

Constraints

Integration Pattern Decision Tree

START — Need to paginate Oracle Fusion REST API
├── < 500 records → Single request, limit=500
├── 500-5,000 → Standard offset/limit loop (count < limit stop)
├── 5,000-50,000 → Date-range partitioning
└── > 50,000 → Do NOT use REST pagination → Use FBDI or BIP

Stop condition: count < limit (NOT hasMore)

Quick Reference

ProblemSymptomFix
Only 500 recordslimit=1000 returns 500Use limit=500 and paginate
Only 25 recordsNo limit specifiedSet limit=500 explicitly
hasMore=false earlytotalResults cappedUse count < limit as stop
Slow at high offsets30+ second responsesDate-range partitioning
Missing recordsData modified during paginationAdd deduplication

Step-by-Step Integration Guide

1. Basic Pagination Loop

Use count < limit as stop condition, NOT hasMore. [src1, src2]

offset = 0
limit = 500
while True:
    data = fetch_page(offset=offset, limit=limit)
    items = data["items"]
    count = data.get("count", len(items))
    all_records.extend(items)
    if count < limit:  # Last page
        break
    offset += limit

2. Date-Range Chunking for Large Datasets

Partition by date to avoid offset degradation. [src4]

3. Get Accurate Total First

Query with limit=0 and totalResults=true for true count. [src1]

Code Examples

Python: Production-Ready Paginator with Retry

# Input:  Oracle Fusion REST API credentials, endpoint
# Output: All records with retry and rate limit handling

class OraclePaginator:
    def __init__(self, base_url, token):
        self.base_url = base_url
        self.token = token

    def extract_all(self, endpoint, query=None):
        all_records, offset, limit = [], 0, 500
        while True:
            params = {"offset": offset, "limit": limit, "onlyData": "true"}
            if query: params["q"] = query
            resp = requests.get(f"{self.base_url}{endpoint}",
                headers={"Authorization": f"Bearer {self.token}"}, params=params)
            resp.raise_for_status()
            data = resp.json()
            items = data.get("items", [])
            all_records.extend(items)
            if data.get("count", len(items)) < limit:
                break
            offset += limit
        return all_records

cURL: Test Pagination Behavior

# Test default (25 records), max (500), and totalResults with offset
curl -s "$ORACLE_URL/fscmRestApi/resources/11.13.18.05/purchaseOrders?limit=500&totalResults=true" \
  -H "Authorization: Bearer $TOKEN" | python -c "
import sys,json; d=json.load(sys.stdin)
print(f'count={d.get(\"count\")}, hasMore={d.get(\"hasMore\")}, totalResults={d.get(\"totalResults\")}')"

Error Handling & Failure Points

Common Errors

CodeMeaningCauseResolution
200 + totalResults mismatchtotalResults cappedBug Doc ID 2728432.1Use count < limit
429Rate limitedToo many requestsExponential backoff
401Token expiredLong pagination runRefresh token
500Server errorHigh offsetUse date-range partitioning

Failure Points

Anti-Patterns

Wrong: Using hasMore as Stop Condition

# ❌ BAD — hasMore is unreliable
if not data.get("hasMore"): break  # May stop at 500 records

Correct: Using count < limit

# ✅ GOOD — count < limit is always reliable
if data.get("count", len(items)) < limit: break

Wrong: Setting limit > 500

# ❌ BAD — Silently returns only 500
resp = requests.get(f"{url}?limit=5000")  # Gets 500, not 5000

Correct: limit=500 with Pagination Loop

# ✅ GOOD — Respect the 500 cap
# Use offset loop with limit=500

Common Pitfalls

Diagnostic Commands

# Test pagination metadata
curl -s "$ORACLE_URL/fscmRestApi/resources/11.13.18.05/purchaseOrders?limit=500&totalResults=true" \
  -H "Authorization: Bearer $TOKEN" | python -c "import sys,json; d=json.load(sys.stdin); print(json.dumps({k:d.get(k) for k in ['count','hasMore','totalResults','limit','offset']}, indent=2))"

# Measure response time at different offsets
for offset in 0 500 1000 5000 10000; do
  time curl -s -o /dev/null "$ORACLE_URL/fscmRestApi/resources/11.13.18.05/purchaseOrders?limit=500&offset=$offset" \
    -H "Authorization: Bearer $TOKEN"
done

Version History & Compatibility

ReleaseDateStatusPagination Changes
25D2025-11CurrentNo changes
25B2025-05SupportedSome HCM offset changes
24D2024-11SupportedtotalResults behavior changed

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
< 5,000 records via REST> 50,000 records extractionFBDI or BI Publisher
Incremental delta syncOne-time migrationFBDI import/export
Real-time search with paginationBulk analyticsOracle Analytics / OTBI

Important Caveats

Related Units