Transports
Configure how Respondent fetches data — HTTP polling, WebSockets, MQTT, S3, and more
Transports define how the feeder service connects to a data source and retrieves raw data. Every source definition requires exactly one transport. The type field selects the transport, and transport-specific fields configure the connection details.
transport:
type: http_poll
url: "https://api.example.com/data"
interval: "60s"
HTTP Poll
HTTP polling is the most common transport. Respondent makes periodic HTTP requests and processes the response body through the parser.
Core fields
type
string
requiredhttp_poll for periodic HTTP requests.url
string
required${VAR} syntax – resolved from RESPONDENT_VAR at load time. For spatial sources, use {lat} and {lon} placeholders.on_demand_url
string{lat} and {lon} placeholders for user-triggered location queries.method
string
default: GETGET, POST.headers
map[string]stringtimeout
duration"10s", "30s", "1m".interval
duration"60s", "5m", "1200s".max_response_bytes
integer
default: 52428800Example
transport:
type: http_poll
url: "https://www.seismicportal.eu/fdsnws/event/1/query?format=json&limit=100"
method: GET
headers:
Accept: "application/json"
timeout: "10s"
interval: "120s"
max_response_bytes: 52428800
Authentication
Respondent supports four authentication patterns. Secrets are never stored in YAML – only environment variable names that are resolved at runtime from RESPONDENT_<env_var>.
Bearer token
transport:
type: http_poll
url: "https://api.example.com/data"
auth:
type: bearer
env_var: "MY_API_TOKEN" # Resolved from RESPONDENT_MY_API_TOKEN
API key in a custom header
transport:
type: http_poll
url: "https://api.example.com/data"
auth:
type: api_key
header: "X-API-Key"
env_var: "MY_API_KEY" # Resolved from RESPONDENT_MY_API_KEY
Credential embedded in URL
No auth block is needed. Use ${VAR} substitution directly in the URL:
transport:
type: http_poll
url: "https://api.example.com/data/${MY_KEY}/endpoint"
OAuth2 token exchange
transport:
type: http_poll
url: "https://api.example.com/data"
auth:
type: oauth2
oauth2:
token_url: "https://auth.example.com/oauth/token"
grant_type: client_credentials # client_credentials | password
credentials:
client_id:
env_var: "MY_CLIENT_ID"
client_secret:
env_var: "MY_CLIENT_SECRET"
# Optional overrides (defaults shown):
# token_header: "Authorization"
# token_prefix: "Bearer "
# refresh_before_expiry: "300s"
# response_mapping:
# access_token: "access_token"
# refresh_token: "refresh_token"
# expires_in: "expires_in"
# extra_params:
# audience: "https://api.example.com"
For the password grant type, add username and password to credentials:
credentials:
client_id:
env_var: "MY_CLIENT_ID"
client_secret:
env_var: "MY_CLIENT_SECRET"
username: "literal-username"
password:
env_var: "MY_PASSWORD"
Each credential field accepts either a literal string value or an env_var reference.
Retry configuration
Configure automatic retries for failed HTTP requests.
retry.max_attempts
integer
default: 0retry.backoff
stringexponential, fixed.retry.initial_delay
duration"2s".retry.max_delay
duration"30s".retry:
max_attempts: 3
backoff: "exponential"
initial_delay: "2s"
max_delay: "30s"
Pagination
For APIs that return paginated results, configure automatic page traversal.
pagination:
type: page_number
page_param: "page"
size_param: "per_page"
size: 100
max_pages: 10
stop_when: "size(records) < 100"
pagination:
type: offset
page_param: "offset"
size_param: "limit"
size: 100
max_pages: 10
pagination:
type: cursor
page_param: "cursor"
size_param: "limit"
size: 100
max_pages: 10
cursor_path: "meta.next_cursor"
pagination.type
string
requiredpage_number, offset, cursor.pagination.page_param
string
requiredpagination.size_param
stringpagination.size
integer
requiredpagination.max_pages
integer
requiredpagination.stop_when
stringrecords is bound to the current page’s record array.pagination.cursor_path
stringtype: cursor.Spatial crawling
For APIs that require geographic coordinates (schema_version 2 only), configure spatial crawling to cover the globe systematically. The URL must contain {lat} and {lon} placeholders.
transport:
type: http_poll
url: "https://api.example.com/data/lat/{lat}/lon/{lon}/dist/250"
spatial:
type: hex_grid
radius_nm: 250
target_refresh: "30s"
batch_size: 10
viewport_priority: true
priority_cap_pct: 30
lat_min: -60
lat_max: 75
transport:
type: http_poll
url: "https://api.example.com/data/lat/{lat}/lon/{lon}/dist/250"
spatial:
type: static_regions
regions:
- lat: 40.7128
lon: -74.0060
label: "New York"
- lat: 51.5074
lon: -0.1278
label: "London"
lat_min and lat_max to skip regions without useful data. For flights, lat_min: -60 and lat_max: 75 prunes Antarctica and the Arctic, reducing initial scan time from ~10 minutes to under 1 minute by eliminating ~40% of grid regions.WebSocket
Persistent WebSocket connection for real-time streaming data.
transport:
type: websocket
url: "wss://stream.example.com/data"
headers:
Origin: "https://example.com"
websocket:
subprotocols: []
subscribe_messages:
- '{"type":"subscribe","channel":"data"}'
ping_interval: "30s"
message_format: text # text | binary
compression: false
origin: "https://example.com"
decode: lzw # Application-level decoding (e.g., Blitzortung LZW)
reconnect:
max_attempts: 0 # 0 = infinite
backoff: exponential
initial_delay: "2s"
max_delay: "60s"
batching:
mode: window
window: "3s"
max_size: 1000
websocket.subscribe_messages
string[]websocket.ping_interval
durationwebsocket.message_format
stringtext, binary.websocket.decode
stringlzw.SSE (Server-Sent Events)
Server-Sent Events stream for unidirectional real-time data.
transport:
type: sse
url: "https://stream.example.com/events"
headers:
Accept: "text/event-stream"
sse:
last_event_id: false
event_filter:
- "message"
- "update"
sse.last_event_id
boolean
default: falsesse.event_filter
string[]MQTT
Subscribe to topics on an MQTT broker.
transport:
type: mqtt
mqtt:
broker: "tcp://broker.example.com:1883"
topics:
- topic: "sensors/+/data"
qos: 1
client_id: "respondent-source"
qos: 1
clean_session: true
version: "3.1.1" # 3.1.1 | 5
keep_alive: 60 # seconds (10-3600)
username: ""
password: ""
tls:
ca_cert: "/path/to/ca.pem"
insecure_skip_verify: false
mqtt.broker
string
required"tcp://broker.example.com:1883".mqtt.topics
array
requiredtopic (supports MQTT wildcards + and #) and an optional per-topic qos override.mqtt.qos
integer
default: 0mqtt.client_id
stringS3 Poll
Poll an S3-compatible bucket for new objects.
transport:
type: s3_poll
interval: "300s"
s3_poll:
endpoint: "https://s3.amazonaws.com"
bucket: "my-data-bucket"
prefix: "exports/"
region: "us-east-1"
access_key: "" # Or use IAM role
secret_key: ""
file_pattern: "*.json"
since_last_modified: "24h"
delete_after_fetch: false
path_style: false # true for MinIO and other S3-compatible stores
s3_poll.endpoint
string
requireds3_poll.bucket
string
requireds3_poll.prefix
strings3_poll.file_pattern
string"*.json".s3_poll.since_last_modified
durations3_poll.path_style
boolean
default: falseFTP / SFTP
Periodic file retrieval from FTP or SFTP servers.
transport:
type: ftp_sftp
interval: "600s"
ftp_sftp:
protocol: sftp # ftp | sftp
host: "ftp.example.com:22"
path: "/data/exports/"
file_pattern: "*.json"
username: "user"
password: ""
private_key: "/path/to/id_rsa" # SFTP only
delete_after_fetch: false
track_seen: true
ftp_sftp.protocol
string
requiredftp, sftp.ftp_sftp.host
string
requiredftp_sftp.path
string
requiredftp_sftp.file_pattern
string"*.json".ftp_sftp.track_seen
booleanWebhook
Inbound HTTP listener for push-style data sources.
transport:
type: webhook
webhook:
listen_addr: ":9090"
path: "/webhooks/my-source"
secret: ""
signature_header: "X-Hub-Signature-256"
signature_algorithm: sha256 # sha256 | sha1
max_body_bytes: 10485760
allowed_ips:
- "192.168.1.0/24"
tls:
cert_file: "/path/to/cert.pem"
key_file: "/path/to/key.pem"
webhook.listen_addr
string
required":9090".webhook.path
string
requiredwebhook.secret
stringwebhook.max_body_bytes
integerKafka
Consume messages from a Kafka topic.
transport:
type: kafka
kafka:
brokers:
- "kafka1.example.com:9092"
- "kafka2.example.com:9092"
topic: "events"
group_id: "respondent-consumer"
start_offset: earliest # earliest | latest
max_bytes: 10485760
commit_interval: "5s"
sasl:
mechanism: SCRAM-SHA-256 # PLAIN | SCRAM-SHA-256 | SCRAM-SHA-512
username: "user"
password: "pass"
tls:
insecure_skip_verify: false
kafka.brokers
string[]
requiredkafka.topic
string
requiredkafka.group_id
string
requiredkafka.start_offset
stringearliest, latest.AMQP
Consume messages from an AMQP (RabbitMQ) exchange or queue.
transport:
type: amqp
amqp:
url: "amqp://user:[email protected]:5672/"
queue: "events"
exchange: "events-exchange"
routing_key: "events.#"
exchange_type: topic # direct | fanout | topic | headers
prefetch_count: 100
auto_ack: false
amqp.url
string
requiredamqp.exchange_type
stringdirect, fanout, topic, headers.amqp.prefetch_count
integerNATS
Subscribe to a NATS subject with optional JetStream durable consumer support.
transport:
type: nats
nats:
url: "nats://nats.example.com:4222"
subject: "events.>"
queue: "respondent-workers"
creds_file: "/path/to/nats.creds"
jetstream:
stream: "EVENTS"
consumer: "respondent"
deliver_policy: new # all | last | new | by_start_time
ack_wait: "30s"
max_deliver: 5
nats.url
string
requirednats.subject
string
required*, >).nats.queue
stringPub/Sub
Subscribe to cloud Pub/Sub (GCP) or Valkey/Redis Pub/Sub channels.
transport:
type: pubsub
pubsub:
provider: gcp # gcp | valkey | redis
# GCP:
project_id: "my-gcp-project"
subscription_id: "my-subscription"
max_outstanding_messages: 1000
# Valkey/Redis:
# addr: "localhost:6379"
# channels: ["events:*"]
# patterns: ["events:*"]
ack_mode: auto # auto | manual
pubsub.provider
string
requiredgcp, valkey, redis.gRPC Stream
Connect to a gRPC server-streaming endpoint.
transport:
type: grpc_stream
grpc_stream:
address: "grpc.example.com:443"
service: "my.package.MyService"
method: "StreamData"
request_json: '{"filter":"active"}'
use_tls: true
metadata:
authorization: "Bearer ${MY_TOKEN}"
grpc_stream.address
string
requiredgrpc_stream.service
string
requiredgrpc_stream.method
string
requiredTCP / UDP
Raw TCP or UDP socket connection, either outbound (connect) or inbound (listen).
transport:
type: tcp_udp
tcp_udp:
protocol: tcp # tcp | udp
mode: connect # connect (outbound) | listen (inbound)
address: "data.example.com:5000"
delimiter: "\n"
max_message_bytes: 65536
buffer_size: 65536
tcp_udp.protocol
string
requiredtcp, udp.tcp_udp.mode
string
requiredconnect opens an outbound connection; listen binds a local listener.tcp_udp.address
string
requiredStreaming behavior
Long-lived transports (WebSocket, SSE, MQTT, Kafka, AMQP, NATS, Pub/Sub, gRPC, TCP/UDP) support batching and reconnection configuration at the transport level.
Batching
Controls how streaming messages are accumulated before being sent to the parser.
batching:
mode: window # per_message | window
window: "3s" # Accumulation window (window mode only)
max_size: 1000 # Max messages per batch (1-100000)
batching.mode
string
requiredper_message processes each message immediately. window accumulates messages for a duration before processing.Reconnection
Defines automatic reconnection behavior when a long-lived connection drops.
reconnect:
max_attempts: 0 # 0 = infinite retries
backoff: exponential # exponential | fixed
initial_delay: "2s"
max_delay: "60s"
reset_after: "10m" # Reset backoff after stable connection
reconnect.max_attempts
integer
default: 0reconnect.reset_after
duration