Requests & Iteration
This page details how to configure HTTP requests and use the iteration feature for looping within Sling API specifications.
Request Configuration
Each endpoint defines its HTTP request details under the request key. These settings merge with and override the defaults.request configuration.
Request Properties
url
Yes
Path (relative to defaults.request.url) or full URL
"users" or "https://api.example.com/users"
method
No
HTTP method (default: "GET")
"POST", "PUT", "PATCH", "DELETE"
headers
No
HTTP headers to send
{"Content-Type": "application/json"}
parameters
No
Query parameters (or form fields for POST)
{"page": 1, "limit": 100}
payload
No
Request body for POST/PUT/PATCH
{"name": "New User"}
timeout
No
Request timeout in seconds (default: 30)
60
rate
No
Max requests per second (default: 2)
5
concurrency
No
Max concurrent requests (default: 5)
10
Example Request Configuration
request:
state:
base_url: https://api.example.com/v1
user_id: 123
url: '{state.base_url}/users/{state.user_id}'
# HTTP Method
method: "POST"
# Headers (merged with defaults.request.headers)
headers:
Content-Type: "application/json"
Authorization: "Bearer {auth.token}"
X-Request-ID: "{uuid()}"
# Query Parameters (or form parameters for POST with application/x-www-form-urlencoded)
parameters:
active: true
department: "{state.department}"
# Request Body (for POST/PUT/PATCH methods)
payload:
user:
name: "New User"
email: "{state.user_email}"
# Request timeout (in seconds)
timeout: 60
# Rate limiting (max requests per second)
rate: 5
# Concurrency (max in-flight requests)
concurrency: 3📝 Note: When
methodisGETorDELETE, theparametersare added as URL query parameters. WhenmethodisPOST,PUT, orPATCHandContent-Typeisapplication/x-www-form-urlencoded, theparametersare sent as form fields.
💡 Tip: Use
{...}expressions to make request values dynamic based on state variables, authentication details, or environment variables.
Iteration (Looping Requests)
The iterate section allows an endpoint to make multiple requests based on a list of items (e.g., IDs from a queue, date ranges).
How Iteration Works
Iteration Properties
over
Yes
Expression to get items to iterate over
"queue.user_ids" or "[1, 2, 3]"
into
Yes
State variable to store current item
"state.current_id"
concurrency
No
Max iterations to run in parallel (default: 10)
5
if
No
Condition to evaluate before starting iteration
"state.should_process == true"
Example: Basic Iteration
iterate:
# Get items from a queue filled by another endpoint
over: "queue.product_ids"
# Store current item in state variable
into: "state.current_product_id"
# Run up to 5 iterations concurrently
concurrency: 5⚠️ Important: Each iteration gets its own state. Changes to state variables in one iteration don't affect other iterations.
Example: Date Range Iteration
This pattern is great for splitting large data extractions into daily chunks:
endpoints:
daily_reports:
state:
# Configure date range (last 7 days by default)
start_date: '{coalesce(env.START_DATE, date_format(date_add(now(), -7, "day"), "%Y-%m-%d"))}'
end_date: '{coalesce(env.END_DATE, date_format(now(), "%Y-%m-%d"))}'
iterate:
# Generate an array of dates between start_date and end_date, one day at a time
over: >
range(
date_parse(state.start_date, "%Y-%m-%d"),
date_parse(state.end_date, "%Y-%m-%d"),
"1d"
)
# Store the current date in state.current_day
into: state.current_day
concurrency: 10
request:
url: '{state.base_url}/reports'
parameters:
# Format the date for the API
date: '{date_format(state.current_day, "%Y-%m-%d")}'Example: Batch Processing with chunk()
chunk()When an API accepts multiple IDs in a single request, use chunk() to process them in batches:
endpoints:
lookup_variants:
iterate:
# Split queue into batches of 50 IDs each
over: "chunk(queue.variant_ids, 50)"
# state.variant_id_batch will be an array of up to 50 IDs
into: "state.variant_id_batch"
concurrency: 5
request:
url: '{state.base_url}/variants/lookup'
parameters:
# Join IDs into comma-separated string
ids: '{join(state.variant_id_batch, ",")}'Using Context Variables for Backfill Ranges
Context variables are runtime values passed from the replication configuration to the API spec. They're particularly useful for supporting both backfill and incremental modes with the same endpoint.
Key context variables:
context.range_start- Start of backfill range (fromsource_options.range)context.range_end- End of backfill range (fromsource_options.range)
Example: Date Range with Context Support
endpoints:
events:
# Persist last processed date for incremental runs
sync: [last_date]
iterate:
# Backfill mode: Use context.range_start/range_end from config
# Incremental mode: Use sync.last_date
over: >
range(
coalesce(context.range_start, sync.last_date, date_format(date_add(now(), -7, "day"), "%Y-%m-%d")),
coalesce(context.range_end, date_format(now(), "%Y-%m-%d")),
"1d"
)
into: "state.current_date"
request:
url: "{state.base_url}/events"
parameters:
date: "{state.current_date}"
response:
records:
jmespath: "events[]"
primary_key: ["event_id"]
processors:
# Track last processed date
- expression: "state.current_date"
output: "state.last_date"
aggregation: "maximum"Replication Config for Backfill:
source: MY_API
target: MY_TARGET_DB
streams:
events:
object: analytics.events
source_options:
# Backfill specific date range
range: '2024-01-01,2024-01-31'Replication Config for Incremental:
source: MY_API
target: MY_TARGET_DB
streams:
events:
object: analytics.events
# No range - uses sync.last_date for incrementalThis pattern allows a single endpoint to handle both historical backfills and ongoing incremental updates seamlessly. See Context Variables for complete details.
Request Flow Control
You can control the request flow using these features:
rate
request
Limit requests per second
rate: 5
concurrency
request
Limit parallel requests
concurrency: 3
concurrency
iterate
Limit parallel iterations
concurrency: 10
if
iterate
Conditional iteration
if: "state.has_data"
timeout
request
Set request timeout
timeout: 30
💡 Tip: Balance performance and API limits:
Too low
concurrency: slower extraction but gentler on the APIToo high
concurrency: faster extraction but may trigger rate limits
iterate.concurrencycontrols parallelism across different IDs/items
request.concurrencycontrols parallelism across paginated requests
Sequences: Setup and Teardown
Endpoints can define optional setup and teardown sequences that run before and after the main requests. These are multi-step workflows perfect for initialization, cleanup, or complex processes.
What are Sequences?
A sequence is an ordered array of API calls. Each call in a sequence can:
Make HTTP requests
Process responses
Update state variables
Use pagination
Execute conditionally
Sequences are used in three places:
Authentication - Custom authentication workflows
Setup - Pre-execution initialization
Teardown - Post-execution cleanup
Sequence Call Structure
Each call in a sequence has the same structure:
- if: <conditional expression> # Optional: Execute only if true
request: <request configuration> # Same as endpoint request
pagination: <pagination config> # Optional: Paginate this call
response: <response configuration> # Process responseSetup Sequences
The setup sequence runs once before the endpoint's main requests/iterations begin. Perfect for:
Fetching configuration data
Initializing session data
Validating prerequisites
Loading dynamic parameters
Basic Setup Example
setup:
- request:
url: "{state.base_url}/config"
method: GET
response:
processors:
- expression: 'jmespath(response.json, "config.api_version")'
output: "state.api_version"
aggregation: last
- expression: 'jmespath(response.json, "config.base_url")'
output: "state.base_url"
aggregation: last
- expression: 'jmespath(response.json, "config.rate_limit")'
output: "state.rate_limit"
aggregation: lastMulti-Step Setup Example
setup:
# Step 1: Get session token
- request:
url: "{state.base_url}/session"
method: POST
payload:
app_id: "{secrets.app_id}"
response:
processors:
- expression: "response.json.session_token"
output: "state.session_token"
aggregation: last
# Step 2: Use session token to get user info
- request:
url: "{state.base_url}/user/me"
headers:
X-Session-Token: "{state.session_token}"
response:
processors:
- expression: 'jmespath(response.json, "user.id")'
output: "state.user_id"
aggregation: last
- expression: 'jmespath(response.json, "user.permissions")'
output: "state.user_permissions"
aggregation: last
# Step 3: Conditionally load admin data
- if: contains(state.user_permissions, "admin")
request:
url: "{state.base_url}/admin/settings"
headers:
X-Session-Token: "{state.session_token}"
response:
processors:
- expression: "response.json.admin_config"
output: "state.admin_config"
aggregation: lastTeardown Sequences
The teardown sequence runs once after all main requests complete. Perfect for:
Closing sessions
Cleaning up temporary resources
Logging completion status
Archiving processed data
Basic Teardown Example
teardown:
- request:
url: "{state.base_url}/session/close"
method: POST
headers:
X-Session-Token: "{state.session_token}"
response:
processors:
- expression: "response.json.status"
output: "state.session_close_status"
aggregation: lastConditional Teardown Example
teardown:
# Only cleanup if we created temporary data
- if: state.created_temp_data == true
request:
url: "{state.base_url}/cleanup"
method: POST
payload:
session_id: "{state.session_id}"
temp_ids: "{state.temp_resource_ids}"
response:
processors:
- expression: 'jmespath(response.json, "cleanup.status")'
output: "state.cleanup_status"
aggregation: last
# Always log final statistics
- request:
url: "{state.base_url}/analytics/log"
method: POST
payload:
endpoint_name: "{env.ENDPOINT_NAME}"
records_processed: "{state.total_records}"
duration_seconds: "{state.duration}"Conditional Execution with if
ifEach call in a sequence can execute conditionally using the if field:
setup:
# Always runs
- request:
url: "{state.base_url}/status"
response:
processors:
- expression: "response.json.api_status"
output: "state.api_status"
aggregation: last
# Only runs if API is in maintenance mode
- if: state.api_status == "maintenance"
request:
url: "{state.base_url}/maintenance/info"
response:
processors:
- expression: "response.json.maintenance_until"
output: "state.maintenance_until"
aggregation: last
- expression: log("API in maintenance until: " + state.maintenance_until)
output: ""Common if Patterns:
# Check state variable existence
if: '!is_null(state.session_token)'
# Check for specific value
if: state.environment == "production"
# Check array membership
if: contains(state.enabled_features, "advanced_mode")
# Multiple conditions
if: state.user_type == "admin" && !is_null(state.admin_token)
# Check environment variable
if: env.ENABLE_FEATURE == "true"Pagination in Sequences
Sequence calls support pagination, useful for multi-page setup data:
setup:
# Load all available categories (paginated)
- request:
url: "{state.base_url}/categories"
parameters:
limit: 100
pagination:
next_state:
offset: "{state.offset + 100}"
stop_condition: "length(response.records) < 100"
response:
records:
jmespath: "categories[]"
processors:
# Collect all category IDs
- expression: "record.id"
output: "queue.category_ids"State Management in Sequences
Important behaviors:
State Isolation: Setup/teardown sequences have their own state copy
State Merging: Changes to state persist back to the main endpoint
Headers Inherited: Request headers from the main endpoint are copied
No Iteration State: Sequences don't have iteration-specific state
endpoints:
my_endpoint:
state:
base_url: "https://api.example.com"
initial_value: 100
setup:
- request:
url: "{state.base_url}/init"
response:
processors:
# This updates the endpoint's state
- expression: "response.json.config_value"
output: "state.config_value"
aggregation: last
request:
# Can now use state.config_value from setup
url: "{state.base_url}/data"
parameters:
config: "{state.config_value}"Complete Sequence Example
Here's a real-world example showing setup, main execution, and teardown:
endpoints:
export_data:
state:
base_url: "https://api.example.com/v2"
setup:
# Step 1: Request export job creation
- request:
url: "{state.base_url}/exports"
method: POST
payload:
format: "csv"
filters:
date_from: "{state.start_date}"
date_to: "{state.end_date}"
response:
processors:
- expression: "response.json.export_id"
output: "state.export_id"
aggregation: last
- expression: log("Created export job: " + response.json.export_id)
output: ""
# Step 2: Poll until export is ready
- request:
url: "{state.base_url}/exports/{state.export_id}/status"
pagination:
next_state:
poll_count: "{coalesce(state.poll_count, 0) + 1}"
stop_condition: >
jmespath(response.json, "status") == "completed" ||
jmespath(response.json, "status") == "failed" ||
state.poll_count >= 60
response:
processors:
- expression: 'jmespath(response.json, "status")'
output: "state.export_status"
aggregation: last
- if: 'jmespath(response.json, "status") == "processing"'
expression: log("Export still processing, poll " + string(state.poll_count))
output: ""
# Step 3: Get download URL (only if completed)
- if: state.export_status == "completed"
request:
url: "{state.base_url}/exports/{state.export_id}"
response:
processors:
- expression: 'jmespath(response.json, "download_url")'
output: "state.download_url"
aggregation: last
# Main request downloads the export file
request:
url: "{state.download_url}"
response:
format: csv
records:
jmespath: "[*]"
teardown:
# Clean up the export job
- request:
url: "{state.base_url}/exports/{state.export_id}"
method: DELETE
response:
processors:
- expression: log("Deleted export job: " + state.export_id)
output: ""Sequence vs. Main Request
When Executes
Before/after main requests
During endpoint execution
Supports Iteration
No
Yes (via iterate)
Supports Pagination
Yes (per call)
Yes
State Scope
Shared with endpoint
Per-iteration (if iterating)
Output Records
No (state only)
Yes (to destination)
Conditional Execution
Yes (via if per call)
Yes (via iterate.if)
Best Practices for Sequences
1. Use Logging for Visibility
setup:
- request:
url: "{state.base_url}/config"
response:
processors:
- expression: "response.json.config"
output: "state.config"
aggregation: last
# Log what we loaded
- expression: log("Loaded config: " + string(state.config))
output: ""2. Handle Errors Gracefully
setup:
- request:
url: "{state.base_url}/optional-config"
response:
rules:
# Don't fail if optional config is missing
- action: continue
condition: "response.status == 404"
message: "Optional config not found, using defaults"3. Keep Sequences Focused
# Good: Each step has a clear purpose
setup:
- request: # Get auth token
- request: # Get user info
- request: # Get feature flags
# Avoid: Mixing unrelated concerns
setup:
- request: # Get auth token
- request: # Process data (should be main request)
- request: # Generate report (should be teardown)4. Use Conditional Steps Wisely
setup:
# Always get config
- request:
url: "{state.base_url}/config"
response:
processors:
- expression: 'jmespath(response.json, "features")'
output: "state.features"
aggregation: last
# Only load beta features if enabled
- if: contains(state.features, "beta")
request:
url: "{state.base_url}/beta/features"📝 Note: Setup/Teardown sequences use the same structure as authentication sequences. State changes persist to the main endpoint, making them perfect for initialization and cleanup tasks.
💡 Tip: Use
log()function liberally in sequences during development to understand the execution flow and debug issues.
Last updated
Was this helpful?