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
- Bottom line: Oracle Fusion REST APIs use offset/limit pagination with a hard 500 record cap per request. totalResults is unreliable with offset, and performance degrades at high offsets.
- Key limit: Max 500 records per request (some endpoints: 499). Default is 25 if limit not specified.
- Watch out for: totalResults caps at current page size when offset is specified — hasMore may return false incorrectly.
- Best for: Integration developers needing reliable REST API pagination for Oracle Fusion Cloud.
- Authentication: Standard OAuth 2.0 — pagination behavior is independent of auth method.
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.
| Property | Value |
| Vendor | Oracle |
| System | Oracle Fusion Cloud (ERP, HCM, CX) |
| Pagination Type | Offset/Limit only (no cursor) |
| Max Records/Page | 500 (some: 499) |
| Default Page Size | 25 |
| Docs | Oracle REST API Reference |
API Surfaces & Capabilities
| Feature | Expected | Actual | Reliable? |
| limit | Set page size | Capped at 500 silently | Within cap |
| offset | Skip N records | Works but degrades | At scale: No |
| hasMore | true if more exist | Can be wrong with offset | No |
| totalResults | Total matching | Caps at page size with offset | No |
| count | Current page count | Accurate | Yes |
| links.next | Next page URL | Usually available | Yes |
Rate Limits & Quotas
Performance by Offset Range
| Offset Range | Response Time | Reliability | Recommendation |
| 0-2,000 | 1-3 seconds | High | Standard pagination |
| 2,000-10,000 | 3-10 seconds | Medium | Filter-based chunking |
| 10,000-50,000 | 10-30 seconds | Low | Date-range partitioning |
| > 50,000 | 30+ sec or timeout | Very low | Use FBDI or BIP |
Constraints
- Max 500 records per request — higher values silently capped with no error.
- totalResults unreliable with offset (Oracle Bug Doc ID 2728432.1).
- hasMore derives from unreliable totalResults — can be wrong.
- No cursor-based pagination — offset/limit only.
- Performance degrades exponentially at high offsets.
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
| Problem | Symptom | Fix |
| Only 500 records | limit=1000 returns 500 | Use limit=500 and paginate |
| Only 25 records | No limit specified | Set limit=500 explicitly |
| hasMore=false early | totalResults capped | Use count < limit as stop |
| Slow at high offsets | 30+ second responses | Date-range partitioning |
| Missing records | Data modified during pagination | Add 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
| Code | Meaning | Cause | Resolution |
| 200 + totalResults mismatch | totalResults capped | Bug Doc ID 2728432.1 | Use count < limit |
| 429 | Rate limited | Too many requests | Exponential backoff |
| 401 | Token expired | Long pagination run | Refresh token |
| 500 | Server error | High offset | Use date-range partitioning |
Failure Points
- totalResults caps at 500 with offset: hasMore returns false prematurely. Fix:
Use count < limit. [src2]
- Silent limit cap at 499: Some HCM endpoints cap at 499. Fix:
Test each endpoint's actual max. [src3]
- Performance collapse at high offsets: 30+ sec responses. Fix:
Date-range partitioning. [src5]
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
- Trusting totalResults with offset: Bug Doc ID 2728432.1. Fix:
Use count < limit. [src2]
- Not specifying limit: Default is 25. Fix:
Always set limit=500. [src1]
- High offset performance: Degrades exponentially. Fix:
Date-range partitioning. [src5]
- Not handling 429: Rate limiting. Fix:
Exponential backoff + Retry-After. [src1]
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
| Release | Date | Status | Pagination Changes |
| 25D | 2025-11 | Current | No changes |
| 25B | 2025-05 | Supported | Some HCM offset changes |
| 24D | 2024-11 | Supported | totalResults behavior changed |
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
| < 5,000 records via REST | > 50,000 records extraction | FBDI or BI Publisher |
| Incremental delta sync | One-time migration | FBDI import/export |
| Real-time search with pagination | Bulk analytics | Oracle Analytics / OTBI |
Important Caveats
- 500-record limit is a hard cap — cannot be increased.
- totalResults unreliable with offset — known Oracle bug persisting for years.
- hasMore derives from unreliable totalResults — do not use as stop condition.
- Offset pagination is not snapshot-isolated — duplicates/gaps possible.
- Different API pillars may have slightly different pagination behavior.
Related Units