Performance Guide
Endpoint Speed Reference
| Endpoint | Speed | Use Case |
|---|---|---|
GET /catalog | ~1s | Browse all authorized Symbols and descriptions |
GET /info/{symbol} | ~0.5s | Get metadata for a specific file |
GET /download/{symbol} | ~0.5s + transfer | Download a specific file |
GET /list | ~2s | Verify file existence with size and timestamp |
GET /count | ~2s | Count total accessible files |
Recommended Workflows
Downloading a known file
If you already know the Symbol:
# Direct download — fastest path
GET /download/IbgePnadAgricultureStk
Discovering and downloading files
# Step 1: Browse catalog (~1s) — find the Symbol you need
GET /catalog
# Returns: [{ "Symbol": "IbgePnadAgricultureStk", "Description": "..." }, ...]
# Step 2: Download by Symbol (~0.5s + transfer)
GET /download/IbgePnadAgricultureStk
Verifying which files exist
# Use /list when you need to confirm files exist in R2 with metadata
GET /list?limit=1000
# ~2s with batched HEAD calls — returns Symbol, size, upload date
Quick file count
GET /count
# ~2s, returns total number of accessible files
Token Caching
Tokens are valid for 1 hour. Do not request a new token on every API call — cache it and refresh when it is ~60 seconds from expiry.
# Good — reuse token until near-expiry
if not self.access_token or self.token_expires_at < datetime.now():
self.access_token = fetch_new_token()
# Bad — fetches a new token on every request
token = fetch_new_token()
call_api(token)
Parallelism
/list and /count internally make batched parallel HEAD requests against R2 — they are already optimized. For downloading multiple files, parallelize client-side:
from concurrent.futures import ThreadPoolExecutor
symbols = ["SymbolA", "SymbolB", "SymbolC"]
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(client.download_file, s, f"{s}.parquet") for s in symbols]
for f in futures:
f.result()