Fetch steps
Fetch steps make HTTP requests to APIs. They're the primary way to retrieve data in Reqon.
HTTP methods
// GET request
get "/users"
// POST request
post "/users" { body: { name: "John" } }
// PUT request
put "/users/123" { body: { name: "Jane" } }
// PATCH request
patch "/users/123" { body: { email: "jane@example.com" } }
// DELETE request
delete "/users/123"
Request options
A fetch option block accepts only these keys: source, body, paginate, until, retry, since, and backfill. Anything else is a parse error.
Query parameters
There's no params: option. Put query parameters directly in the path string:
get "/users?limit=100&offset=0&status=active"
Request body
post "/users" {
body: {
name: "John Doe",
email: "john@example.com",
roles: ["user", "admin"]
}
}
Headers
You can't set per-request headers on a fetch step. Headers are declared once on the source, in its headers block, and apply to every request through that source:
source API {
auth: bearer,
base: "https://api.example.com",
headers: {
"Accept": "application/json",
"X-Custom-Header": "value"
}
}
Pagination
Offset-based
get "/users" {
paginate: offset(page, 100),
until: length(response) == 0
}
Parameters:
page- Query parameter name for offset value100- Page size
Page number-based
get "/users" {
paginate: page(pageNum, 50),
until: response.meta.hasNext == false
}
Parameters:
pageNum- Query parameter name for page number50- Page size
Cursor-based
get "/users" {
paginate: cursor(cursor, 100, "meta.nextCursor"),
until: response.meta.nextCursor == null
}
Parameters:
cursor- Query parameter name100- Page size"meta.nextCursor"- Path to next cursor in response
See Pagination for detailed documentation.
Termination conditions
The until option specifies when to stop paginating:
// Stop when empty response
get "/users" {
paginate: offset(page, 100),
until: length(response) == 0
}
// Stop when no more pages
get "/users" {
paginate: page(p, 50),
until: response.pagination.hasNext == false
}
// Stop when cursor is null
get "/users" {
paginate: cursor(c, 100, "nextCursor"),
until: response.nextCursor == null
}
// Stop after N items
get "/users" {
paginate: offset(page, 100),
until: length(response) == 0 or page > 10
}
Retry configuration
get "/users" {
retry: {
maxAttempts: 3,
backoff: exponential,
initialDelay: 1000,
maxDelay: 30000
}
}
Options:
maxAttempts- Maximum retry attempts (default 3)backoff- Strategy:exponential,linear, orconstant(defaultexponential)initialDelay- First retry delay in milliseconds (default 1000)maxDelay- Maximum delay between retries in millisecondstimeout- Per-request timeout in milliseconds
See Retry Strategies for details.
Incremental sync
Fetch only changes since last run:
get "/users" {
since: lastSync
}
This automatically adds a timestamp parameter to the request.
See Incremental Sync for details.
Response handling
The response variable is automatically set after each fetch:
action FetchUsers {
get "/users"
// response contains the parsed JSON body
for user in response.data {
store user -> users { key: .id }
}
}
Response structure
action InspectResponse {
get "/users"
// Access body data
store response.users -> users { key: .id }
// Check response metadata
validate response {
assume response.total > 0
}
}
Named source requests
When you have multiple sources, use the source option to pick one. The first source defined is the default:
mission MultiSource {
source Primary { auth: bearer, base: "https://primary.api.com" }
source Secondary { auth: bearer, base: "https://secondary.api.com" }
action FetchBoth {
// Default source (first defined)
get "/users"
// Explicit source
get "/backup-users" { source: Secondary }
}
}
Dynamic paths
Interpolate values into a path with {...}. The names resolve against the current loop variable and action variables:
action FetchUserOrders {
for user in users {
get "/users/{user.id}/orders"
store response -> orders { key: .id }
}
}
You can also build the path with the + operator, which concatenates strings:
get "/users/" + user.id + "/orders"
OpenAPI operation calls
When using OAS sources, use call syntax. The path and its parameters come from the spec and from interpolated context, so a call takes only the standard fetch options (no params):
source Petstore from "./petstore.yaml" { auth: bearer }
action FetchPets {
call Petstore.listPets
call Petstore.getPetById
}
See OpenAPI Integration for details.
Complete example
mission DataSync {
source API {
auth: bearer,
base: "https://api.example.com/v1"
}
store users: file("users")
action FetchAllUsers {
get "/users?include=profile" {
paginate: offset(offset, 100),
until: length(response.users) == 0,
retry: {
maxAttempts: 3,
backoff: exponential,
initialDelay: 1000
},
since: lastSync
}
for user in response.users {
validate user {
assume .id is string,
assume .email is string
}
store user -> users { key: .id, upsert: true }
}
}
run FetchAllUsers
}