HTTP Request Execution
Section titled “HTTP Request Execution”Execute outbound HTTP requests from a Hoody container using libcurl, and stream async job lifecycle events over WebSocket or Server-Sent Events. The POST endpoint exposes the full request surface (retry, proxy, cookie sessions, scheduling, response storage), while the GET endpoint is a quick URL-driven form for simple calls. Subscribe to /api/v1/curl/ws or /api/v1/curl/sse to observe jobs started in async mode, or open the persistent /api/v1/curl/channel WebSocket for multiplexed request dispatch.
Execute HTTP requests
Section titled “Execute HTTP requests”POST /api/v1/curl/request
Section titled “POST /api/v1/curl/request”Execute an HTTP request using libcurl with comprehensive configuration options. Supports both synchronous (immediate response) and asynchronous (background job) execution modes.
Execution modes:
sync(default) — blocks until completion and returns the response in the same call.async— returns ajob_idimmediately and runs the request in the background.
Response modes:
transparent(default) — returns the raw upstream response with original headers.json— wraps the response in JSON with timing metrics and metadata.
This endpoint takes no path, query, or header parameters.
Request Body
Section titled “Request Body”The request body follows the curl_CurlRequest schema. The url field is required; every other field is optional. Unknown fields are rejected (deny_unknown_fields).
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Target URL. |
method | string | No | HTTP method (for example GET, POST). |
mode | string | No | Execution mode: sync or async. |
response | string | No | Response mode: transparent or json. |
headers | object | No | Custom headers keyed by header name. |
data | string | No | Raw request body. |
json | object | No | JSON request body of any shape. Sent with Content-Type: application/json. |
form | object | No | Form fields keyed by field name. |
bearer_token | string | No | Bearer token sent as Authorization: Bearer .... |
auth_method | string | No | HTTP authentication method. |
auth_user | string | No | HTTP authentication username. |
auth_password | string | No | HTTP authentication password. |
cookie | string | No | Cookie header value. |
user_agent | string | No | User-Agent header. |
referer | string | No | Referer header. |
follow_redirects | boolean | No | Follow HTTP redirects. |
max_redirects | integer | No | Maximum number of redirects to follow. |
timeout | integer | No | Total request timeout (seconds). |
connect_timeout | integer | No | Connection timeout (seconds). |
insecure | boolean | No | Skip TLS verification. |
compressed | boolean | No | Request a compressed response. |
keepalive | boolean | No | Enable TCP keepalive. |
keepalive_time | integer | No | TCP keepalive idle time (seconds). |
tcp_nodelay | boolean | No | Disable Nagle’s algorithm. |
proxy | string | No | Proxy URL. |
proxy_user | string | No | Proxy username. |
proxy_password | string | No | Proxy password. |
cacert | string | No | CA certificate path or content. |
cert | string | No | Client certificate path or content. |
cert_type | string | No | Client certificate type (for example PEM). |
key | string | No | Client private key. |
session_id | string | No | Reuse cookies from this session ID. |
save | boolean | No | Save the response body to container storage. |
save_path | string | No | Relative save path under downloads/by-job/{job_id}. Must not be absolute or contain ... |
job_name | string | No | Friendly name for the async job. |
schedule | string | No | Cron expression for recurring execution. |
range | string | No | Byte range, for example 0-1023. |
speed_limit | integer | No | Download speed limit (bytes/sec). |
speed_time | integer | No | Time window (seconds) for speed_limit. |
max_filesize | integer | No | Maximum response size in bytes. |
retry_count | integer | No | Number of retries on failure. |
retry_delay | integer | No | Delay between retries (seconds). |
Example
Section titled “Example”curl -X POST 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com/api/v1/curl/request' \ -H 'Authorization: Bearer <HOODY_TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://api.example.com/v1/posts", "method": "POST", "headers": { "Accept": "application/json" }, "json": { "title": "Hello world", "published": true }, "mode": "sync", "response": "json", "retry_count": 2, "retry_delay": 1, "bearer_token": "sk_test_4eC39HqLyjWDarjtT1zdp7dc" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com', token: process.env.HOODY_TOKEN,});
await client.curl.execute({ url: 'https://api.example.com/v1/posts', method: 'POST', json: { title: 'Hello world', published: true }, bearer_token: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc', retry_count: 2, retry_delay: 1,});Responses
Section titled “Responses”{ "success": true, "status_code": 201, "headers": { "content-type": "application/json; charset=utf-8", "x-request-id": "req_8f7c2b1a" }, "body": "{\"id\":\"post_01HMV\",\"title\":\"Hello world\",\"published\":true}", "is_binary": false, "job_id": null, "metadata": { "content_type": "application/json; charset=utf-8", "effective_url": "https://api.example.com/v1/posts", "redirect_count": 0, "size_download": 58, "size_upload": 41, "speed_download": 1284.5, "speed_upload": 902.3 }, "timing": { "total": 0.184, "namelookup": 0.012, "connect": 0.041, "pretransfer": 0.044, "starttransfer": 0.176, "redirect": 0.0 }}| Field | Type | Required | Description |
|---|---|---|---|
success | boolean | Yes | Whether the upstream request completed without libcurl errors. |
status_code | integer | Yes | HTTP status code returned by the upstream. |
headers | object | Yes | Response headers keyed by header name. |
body | string | Yes | UTF-8 text for text responses, or base64 when is_binary is true. |
is_binary | boolean | Yes | true when body is base64-encoded; false when it is plain UTF-8. |
job_id | string | No | Job ID when the request was executed asynchronously. |
metadata | object | Yes | Per-response metadata — see curl_ResponseMetadata. |
timing | object | Yes | Per-response timing — see curl_ResponseTiming. |
{ "job_id": "01HMZ8X9K2QF3N5P7R8T6V4WYD", "status": "queued", "name": "nightly-export"}{ "error": "INVALID_URL", "message": "Invalid URL format"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_URL | Malformed URL | The provided URL is not in valid format | Provide a complete URL with protocol (e.g., https://example.com) |
INVALID_PARAMETER | Invalid parameter value | One or more parameters contain invalid values | Check parameter types and allowed values in API documentation |
{ "error": "NETWORK_ERROR", "message": "Connection timeout after 30 seconds"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NETWORK_ERROR | Network request failed | cURL could not complete the HTTP request | Check target URL is accessible, verify network connectivity, check timeout settings |
GET /api/v1/curl/request
Section titled “GET /api/v1/curl/request”Execute a simple HTTP request using URL query parameters. Best suited for quick GET requests and one-off tests. For advanced features (retry, schedule, proxy, cookie sessions) use the POST endpoint.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
url | query | string | Yes | Target URL (required) |
method | query | string | No | HTTP method (default: GET) |
response | query | string | No | Response mode: transparent or json (default: json) |
mode | query | string | No | Execution mode: sync or async (default: sync) |
session_id | query | string | No | Session ID for cookie persistence |
follow_redirects | query | boolean | No | Follow redirects (default: true) |
timeout | query | integer | No | Timeout in seconds |
user_agent | query | string | No | User-Agent header |
referer | query | string | No | Referer header |
bearer_token | query | string | No | Bearer token |
save | query | boolean | No | Save to storage |
save_path | query | string | No | Custom save path, relative to downloads/by-job/{job_id}. No absolute paths or .. |
insecure | query | boolean | No | Allow insecure SSL |
compressed | query | boolean | No | Request compressed |
job_name | query | string | No | Job name for async |
data | query | string | No | Raw request body (curl --data); alias body; presence upgrades default method to POST |
json | query | string | No | JSON request body, sent with Content-Type: application/json (curl --json); upgrades default method to POST |
header | query | array | No | Custom header as Name: Value. Repeatable — supply once per header |
data_base64 | query | string | No | Base64 request body (binary-safe; standard or URL-safe); alias body_base64. Takes precedence over data/json; upgrades default method to POST |
Example
Section titled “Example”curl 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com/api/v1/curl/request?url=https%3A%2F%2Fapi.example.com%2Fposts%2F42&method=GET&response=json' \ -H 'Authorization: Bearer <HOODY_TOKEN>'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com', token: process.env.HOODY_TOKEN,});
await client.curl.executeCurlRequestGet({ url: 'https://api.example.com/posts/42', method: 'GET', response: 'json',});Responses
Section titled “Responses”{ "success": true, "status_code": 200, "headers": { "content-type": "application/json" }, "body": "{\"id\":42,\"title\":\"Hello world\"}", "is_binary": false, "job_id": null, "metadata": { "content_type": "application/json", "effective_url": "https://api.example.com/posts/42", "redirect_count": 0, "size_download": 30, "size_upload": 0, "speed_download": 950.2, "speed_upload": 0 }, "timing": { "total": 0.092, "namelookup": 0.008, "connect": 0.034, "pretransfer": 0.036, "starttransfer": 0.088, "redirect": 0.0 }}{ "job_id": "01HMZ8X9K2QF3N5P7R8T6V4WYD", "status": "queued"}{ "error": "INVALID_URL", "message": "Missing required parameter: url"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_URL | Missing or invalid URL | URL parameter is required and must be valid | Provide url parameter with complete URL including protocol |
INVALID_PARAMETER | Invalid query parameter | One or more query parameters have invalid values | Check parameter values match expected types (e.g., timeout as number) |
{ "error": "NETWORK_ERROR", "message": "Network connection failed"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NETWORK_ERROR | Network request failed | Failed to execute HTTP request via cURL | Verify target URL accessibility and network connectivity |
Stream job events
Section titled “Stream job events”GET /api/v1/curl/ws
Section titled “GET /api/v1/curl/ws”Establish a WebSocket connection that streams async job lifecycle events as JSON messages. Use this for live progress updates of jobs created via the execution endpoints.
Messages:
jobstarted—{job_id, name}jobprogress—{job_id, progress}(progress is a fraction in the range 0 to 1, inclusive)jobcompleted—{job_id, status}error—{message}
Pass ?job_id= to receive events for a single job only.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
job_id | query | string | No | Optional job ID filter |
Example
Section titled “Example”curl --include --no-buffer \ -H 'Connection: Upgrade' \ -H 'Upgrade: websocket' \ -H 'Sec-WebSocket-Version: 13' \ -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \ 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com/api/v1/curl/ws?job_id=01HMZ8X9K2QF3N5P7R8T6V4WYD'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com', token: process.env.HOODY_TOKEN,});
await client.curl.events.streamWs('01HMZ8X9K2QF3N5P7R8T6V4WYD');Responses
Section titled “Responses”HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo={ "statusCode": 400, "error": "Bad Request", "message": "Invalid WebSocket upgrade request"}{ "statusCode": 500, "error": "Internal Server Error", "message": "Failed to establish event stream"}GET /api/v1/curl/sse
Section titled “GET /api/v1/curl/sse”Establish a Server-Sent Events connection for job lifecycle events. Each event is delivered as a standard SSE frame with event: set to the message type and data: containing the JSON payload.
Events:
jobstarted—{job_id, name}jobprogress—{job_id, progress}jobcompleted—{job_id, status}error—{message}lagged—{missed}(emitted if the broadcast subscriber falls behind)
Pass ?job_id= to receive events for a single job only. The Last-Event-Id header is accepted for spec compliance but ignored — there is no replay buffer.
The connection counts against the global SSE concurrency cap. When exhausted the server returns 503.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
job_id | query | string | No | Optional job ID filter |
Example
Section titled “Example”curl --no-buffer \ -H 'Accept: text/event-stream' \ -H 'Authorization: Bearer <HOODY_TOKEN>' \ 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com/api/v1/curl/sse?job_id=01HMZ8X9K2QF3N5P7R8T6V4WYD'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com', token: process.env.HOODY_TOKEN,});
await client.curl.events.sseJobEvents({ job_id: '01HMZ8X9K2QF3N5P7R8T6V4WYD' });Responses
Section titled “Responses”event: jobstarteddata: {"job_id":"01HMZ8X9K2QF3N5P7R8T6V4WYD","name":"nightly-export"}
event: jobprogressdata: {"job_id":"01HMZ8X9K2QF3N5P7R8T6V4WYD","progress":0.42}
event: jobcompleteddata: {"job_id":"01HMZ8X9K2QF3N5P7R8T6V4WYD","status":"success"}HTTP/1.1 503 Service UnavailableRetry-After: 5Content-Type: text/plain
SSE concurrency cap exhaustedMultiplexed execution channel
Section titled “Multiplexed execution channel”GET /api/v1/curl/channel
Section titled “GET /api/v1/curl/channel”Establish a persistent WebSocket channel for multiplexed, validated CurlRequest execution. The server validates each request before dispatching.
This endpoint is separate from /api/v1/curl/ws, which only streams async job lifecycle events.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
max_concurrent | query | integer | No | Alias for max concurrent streams on this channel connection |
max_concurrent_streams | query | integer | No | Maximum concurrently executing streams on this channel connection |
max_pool | query | integer | No | Alias for max_concurrent; does not configure outbound libcurl connection pooling |
max_queue | query | integer | No | Maximum queued streams waiting for a per-connection execution slot |
max_frame_bytes | query | integer | No | Maximum inbound WebSocket text frame size in bytes |
max_request_bytes | query | integer | No | Maximum assembled request JSON size in bytes |
chunk_bytes | query | integer | No | Maximum upstream response bytes encoded into one channel body frame |
stream_timeout_secs | query | integer | No | Per-stream execution timeout in seconds |
idle_timeout_secs | query | integer | No | Idle channel timeout in seconds |
max_outbound_messages | query | integer | No | Maximum queued outbound channel messages |
Example
Section titled “Example”curl --include --no-buffer \ -H 'Connection: Upgrade' \ -H 'Upgrade: websocket' \ -H 'Sec-WebSocket-Version: 13' \ -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \ 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com/api/v1/curl/channel?max_concurrent=4&max_queue=32&max_frame_bytes=65536&idle_timeout_secs=120'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://67e89abc123def456789abcd-890abcdef12345678901cdef-curl-1.node-us.containers.hoody.com', token: process.env.HOODY_TOKEN,});
await client.curl.events.wsRequestChannel({ max_concurrent: 4, max_queue: 32, max_frame_bytes: 65536, idle_timeout_secs: 120,});Responses
Section titled “Responses”HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo={ "statusCode": 400, "error": "Bad Request", "message": "Invalid WebSocket upgrade request"}{ "statusCode": 500, "error": "Internal Server Error", "message": "Failed to establish channel"}