Pagination
All list endpoints (trades, accounts, playbooks) support offset/page-based pagination to handle large datasets efficiently.
Pagination parameters
Use either offset or page to paginate. When both are provided, offset takes precedence.
Example requests
Get first page (default):
curl "https://app.mypropjournal.com/api/v1/trades" \
-H "Authorization: Bearer mpj_your_api_key_here"
Get next page with offset:
curl "https://app.mypropjournal.com/api/v1/trades?offset=20&limit=25" \
-H "Authorization: Bearer mpj_your_api_key_here"
JavaScript:
async function getPage(endpoint, offset = 0, limit = 20) {
const url = new URL(`https://app.mypropjournal.com/api/v1/${endpoint}`);
url.searchParams.append('offset', offset);
url.searchParams.append('limit', limit);
const response = await fetch(url, {
headers: {
'Authorization': 'Bearer mpj_your_api_key_here',
'Content-Type': 'application/json'
}
});
return response.json();
}
// Get second page (items 21-45)
const page2 = await getPage('trades', 20, 25);
Python:
import requests
def get_page(endpoint, offset=0, limit=20):
url = f'https://app.mypropjournal.com/api/v1/{endpoint}'
headers = {
'Authorization': 'Bearer mpj_your_api_key_here',
'Content-Type': 'application/json'
}
params = {'offset': offset, 'limit': limit}
response = requests.get(url, headers=headers, params=params)
response.raise_for_status()
return response.json()
# Get items 101-200 (offset 100, limit 100)
page = get_page('trades', offset=100, limit=100)
Pagination response object
Every paginated response includes a pagination object with metadata:
{
"data": [...],
"pagination": {
"total": 250,
"limit": 25,
"offset": 20,
"has_more": true
}
}
Pagination fields
Fetching all pages
JavaScript example:
async function getAllTrades() {
let allTrades = [];
let offset = 0;
let hasMore = true;
const limit = 100;
while (hasMore) {
const response = await getPage('trades', offset, limit);
allTrades = allTrades.concat(response.data);
hasMore = response.pagination.has_more;
offset += response.data.length;
console.log(`Fetched offset ${offset - response.data.length}: ${response.data.length} trades`);
}
return allTrades;
}
const allTrades = await getAllTrades();
console.log(`Total trades fetched: ${allTrades.length}`);
Python example:
def get_all_trades():
all_trades = []
offset = 0
has_more = True
limit = 100
while has_more:
response = get_page('trades', offset=offset, limit=limit)
all_trades.extend(response['data'])
has_more = response['pagination']['has_more']
offset += len(response['data'])
print(f"Fetched offset {offset - len(response['data'])}: {len(response['data'])} trades")
return all_trades
all_trades = get_all_trades()
print(f"Total trades fetched: {len(all_trades)}")
Pagination best practices
- Use the maximum limit (100) when fetching all data to minimize API calls
- Check
has_moreto determine if more pages exist - Handle rate limits gracefully with exponential backoff (when implemented)
- Cache responses if data doesn't change frequently
- Show progress to users when fetching multiple pages
Error handling
Standard error response
All errors follow a consistent JSON structure:
{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE",
"details": {
"field_name": "Specific validation error"
}
}
HTTP status codes
Common error codes
Error examples
401 Unauthorized - Invalid API key:
{
"error": "Invalid API key",
"code": "INVALID_API_KEY"
}
403 Forbidden - Subscription required:
{
"error": "API access requires an active paid subscription",
"code": "SUBSCRIPTION_REQUIRED"
}
400 Bad Request - Validation error:
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"details": {
"entry_price": "Must be a positive number",
"side": "Must be 'long' or 'short'",
"symbol": "Required field"
}
}
404 Not Found - Resource not found:
{
"error": "Trade not found",
"code": "NOT_FOUND"
}
500 Internal Server Error:
{
"error": "An unexpected error occurred. Please try again later.",
"code": "INTERNAL_ERROR"
}
Error handling patterns
JavaScript/TypeScript
async function getTrade(tradeId) {
try {
const response = await fetch(
`https://app.mypropjournal.com/api/v1/trades/${tradeId}`,
{
headers: {
'Authorization': 'Bearer mpj_your_api_key_here',
'Content-Type': 'application/json'
}
}
);
if (!response.ok) {
const error = await response.json();
switch (response.status) {
case 401:
console.error('Invalid API key');
break;
case 403:
console.error('Subscription required for API access');
break;
case 404:
console.error('Trade not found or access denied');
break;
default:
console.error(`Error: ${error.error}`);
}
throw new Error(error.error);
}
return response.json();
} catch (err) {
console.error('Request failed:', err.message);
throw err;
}
}
Python
import requests
from requests.exceptions import HTTPError
def get_trade(trade_id):
url = f'https://app.mypropjournal.com/api/v1/trades/{trade_id}'
headers = {
'Authorization': 'Bearer mpj_your_api_key_here',
'Content-Type': 'application/json'
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()
except HTTPError as err:
error_data = err.response.json()
error_code = error_data.get('code')
error_message = error_data.get('error')
if err.response.status_code == 401:
print('Invalid API key')
elif err.response.status_code == 403:
print('Subscription required for API access')
elif err.response.status_code == 404:
print('Trade not found or access denied')
else:
print(f'Error: {error_message}')
raise
except requests.exceptions.RequestException as err:
print(f'Request failed: {err}')
raise
Retry logic with exponential backoff
For transient errors (5xx status codes), implement retry logic:
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Don't retry on 4xx errors (client errors)
if (response.status >= 400 && response.status < 500) {
return response;
}
// Retry on 5xx errors
if (response.status >= 500) {
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
console.log(`Server error, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
return response;
} catch (err) {
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Request failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw err;
}
}
}
}
Debugging tips
- Check the
detailsfield in validation errors for specific field-level messages - Verify API key format starts with
mpj_ - Confirm subscription status if receiving 403 errors
- Log full error responses during development for better debugging
- Test with cURL first to isolate issues from your application code
Example debugging command
# Test API key validity
curl -v "https://app.mypropjournal.com/api/v1/trades" \
-H "Authorization: Bearer mpj_your_api_key_here" \
2>&1 | grep -E "< HTTP|error|code"