Proxy execute
Proxy execute lets you call any HTTP endpoint on a supported toolkit using a connected account. Composio injects the authentication (OAuth token, API key, basic auth, etc.) on the server side, so your code never handles raw credentials.
Use it when you need an endpoint that Composio's predefined tools do not cover, or when you want the flexibility of a raw HTTP call while keeping Composio as the source of truth for user auth.
Proxy execute is a form of direct tool execution. It bypasses session state, tool schemas, and modifiers. If you are building an agent, prefer sessions — use the proxy only for the specific API call that isn't available as a tool.
When to use proxy execute
1. Endpoints not covered by a predefined tool
You need a specific endpoint on a toolkit (for example, an unusual LinkedIn or Notion endpoint) that isn't exposed as a predefined Composio tool. Instead of extracting the raw OAuth token and making the call yourself, send the request through proxy execute and let Composio attach credentials.
# Fetch the authenticated user's LinkedIn profile
response = composio.tools.proxy(
endpoint="/v2/userinfo",
method="GET",
connected_account_id="ca_linkedin_user_123",
)
print(response["data"])// Fetch the authenticated user's LinkedIn profile
const { data } = await composio.tools.proxyExecute({
endpoint: '/v2/userinfo',
method: 'GET',
connectedAccountId: 'ca_linkedin_user_123',
});
console.log(data);2. Request shapes a predefined tool cannot express
AI workflows that talk to Gmail, Drive, Sheets, Outlook, or Teams often need request shapes beyond the pre-built actions — custom query parameters, partial field masks, advanced filters. Proxy execute gives you the full HTTP surface of the upstream API while keeping auth managed by Composio.
# Read a Google Sheet range with custom render options
response = composio.tools.proxy(
endpoint="/v4/spreadsheets/1abc.../values/Sheet1!A1:D100",
method="GET",
connected_account_id="ca_googlesheets_user_123",
parameters=[
{"name": "valueRenderOption", "value": "UNFORMATTED_VALUE", "type": "query"},
{"name": "dateTimeRenderOption", "value": "SERIAL_NUMBER", "type": "query"},
],
)
print(response["data"])// Read a Google Sheet range with custom render options
const { data } = await composio.tools.proxyExecute({
endpoint: '/v4/spreadsheets/1abc.../values/Sheet1!A1:D100',
method: 'GET',
connectedAccountId: 'ca_googlesheets_user_123',
parameters: [
{ name: 'valueRenderOption', value: 'UNFORMATTED_VALUE', in: 'query' },
{ name: 'dateTimeRenderOption', value: 'SERIAL_NUMBER', in: 'query' },
],
});
console.log(data);3. Terminal and CLI agents that would otherwise use raw tokens
Agents running in a terminal often fall back to curl calls with a hardcoded bearer token. Replacing that with a call to Composio's proxy endpoint keeps user credentials server-side — no tokens in shell history, env files, or process state.
curl --location 'https://backend.composio.dev/api/v3.1/tools/execute/proxy' \
--header "x-api-key: $COMPOSIO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"endpoint": "/user/repos",
"method": "GET",
"connected_account_id": "ca_github_user_123",
"parameters": [
{ "name": "per_page", "value": "50", "type": "query" },
{ "name": "sort", "value": "updated", "type": "query" }
]
}'Quick start
from composio import Composio
composio = Composio(api_key="your_api_key")
response = composio.tools.proxy(
endpoint="/repos/composiohq/composio/issues/1",
method="GET",
connected_account_id="ca_github_user_123",
parameters=[
{"name": "Accept", "value": "application/vnd.github.v3+json", "type": "header"},
],
)
print(response["status"])
print(response["data"])import { Composio } from '@composio/core';
const composio = new Composio({ apiKey: 'your_api_key' });
const { status, data } = await composio.tools.proxyExecute({
endpoint: '/repos/composiohq/composio/issues/1',
method: 'GET',
connectedAccountId: 'ca_github_user_123',
parameters: [
{ name: 'Accept', value: 'application/vnd.github.v3+json', in: 'header' },
],
});
console.log(status);
console.log(data);The endpoint can be an absolute URL (https://api.example.com/v1/resource) or a relative path (/v1/resource). Relative paths are resolved against the toolkit's default base URL — only use the absolute form when calling a host that isn't the toolkit's standard API (for example, a regional Salesforce or Zendesk domain).
Proxy execute rejects cross-domain requests. The endpoint must resolve to the same domain as the connected account's toolkit (for example, a GitHub connection can only call api.github.com paths). Pointing the proxy at an arbitrary third-party host will fail — this is an intentional security boundary, not a quota, so it cannot be bypassed by adjusting the request.
Parameters
| Parameter | Required | Type | Description |
|---|---|---|---|
endpoint | Yes | string | Absolute URL or path relative to the toolkit's base URL. |
method | Yes | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | HTTP verb. |
connected_account_id / connectedAccountId | No | string | Which connected account to use for auth. Defaults to the most recent active account for the project. |
body | No | object | JSON request body. Used with POST, PUT, and PATCH. |
parameters | No | Array<{ name, value, type }> | Extra headers or query parameters. type is "header" or "query" (Python) / in is "header" or "query" (TypeScript). |
binary_body | No | { url } | { base64, content_type? } | Binary upload payload. Either a URL to fetch or base64-encoded bytes (up to 4 MB). |
The full schema is documented in the POST /api/v3.1/tools/execute/proxy reference.
Response shape
{
"data": { /* parsed JSON body returned by the upstream API */ },
"status": 200,
"headers": { "content-type": "application/json", "...": "..." },
"binary_data": {
"url": "https://...",
"content_type": "application/pdf",
"size": 12345,
"expires_at": "2026-01-01T00:00:00Z"
}
}binary_data is only present when the upstream API returns a binary response (PDFs, images, etc.). See Binary data support for details.
Common patterns
GET with query parameters
response = composio.tools.proxy(
endpoint="/search/issues",
method="GET",
connected_account_id="ca_github_user_123",
parameters=[
{"name": "q", "value": "is:open label:bug repo:composiohq/composio", "type": "query"},
{"name": "per_page", "value": "20", "type": "query"},
],
)const { data } = await composio.tools.proxyExecute({
endpoint: '/search/issues',
method: 'GET',
connectedAccountId: 'ca_github_user_123',
parameters: [
{ name: 'q', value: 'is:open label:bug repo:composiohq/composio', in: 'query' },
{ name: 'per_page', value: '20', in: 'query' },
],
});POST with a JSON body
response = composio.tools.proxy(
endpoint="/repos/composiohq/composio/issues",
method="POST",
connected_account_id="ca_github_user_123",
body={
"title": "Found a bug",
"body": "Steps to reproduce: ...",
"labels": ["bug"],
},
)const { data } = await composio.tools.proxyExecute({
endpoint: '/repos/composiohq/composio/issues',
method: 'POST',
connectedAccountId: 'ca_github_user_123',
body: {
title: 'Found a bug',
body: 'Steps to reproduce: ...',
labels: ['bug'],
},
});Custom headers
Add vendor-specific headers (API versions, accept types, correlation IDs) via parameters with type: "header".
response = composio.tools.proxy(
endpoint="/crm/v3/objects/contacts",
method="GET",
connected_account_id="ca_hubspot_user_123",
parameters=[
{"name": "Accept", "value": "application/json", "type": "header"},
{"name": "X-Request-Id", "value": "req_abc123", "type": "header"},
{"name": "limit", "value": "100", "type": "query"},
],
)const { data } = await composio.tools.proxyExecute({
endpoint: '/crm/v3/objects/contacts',
method: 'GET',
connectedAccountId: 'ca_hubspot_user_123',
parameters: [
{ name: 'Accept', value: 'application/json', in: 'header' },
{ name: 'X-Request-Id', value: 'req_abc123', in: 'header' },
{ name: 'limit', value: '100', in: 'query' },
],
});Do not set the Authorization header yourself — Composio injects the correct one based on the connected account's auth scheme. Setting it manually will override Composio's credential and usually produces a 401.
Uploading a file
Use binary_body to upload binary content. You can point at a URL that Composio fetches server-side, or inline the bytes as base64.
curl --location 'https://backend.composio.dev/api/v3.1/tools/execute/proxy' \
--header "x-api-key: $COMPOSIO_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"endpoint": "/upload",
"method": "POST",
"connected_account_id": "ca_abc123",
"binary_body": { "url": "https://example.com/report.pdf" }
}'binary_body posts the file as the request body with a single Content-Type header. It does not build a multipart/form-data payload, so APIs that require multipart uploads (for example, Twitter / X media upload, Slack files.upload, some Google upload endpoints) are not supported today. For those, wrap the call in a custom tool that constructs the multipart body yourself.
See the binary data changelog entry for the full upload and download flow.
Error handling
Proxy execute forwards the upstream response verbatim — status, headers, and data reflect what the toolkit API returned. Check status and branch on the common failures.
| Status | Typical cause | How to resolve |
|---|---|---|
400 Bad Request | Malformed endpoint path, invalid body, or unsupported method. | Check the upstream API docs for the expected request shape; proxy execute does not validate upstream schemas. |
401 Unauthorized | The connected account's token expired, was revoked, or is for the wrong project. | Re-authenticate the user, or import fresh credentials. Verify the connected_account_id belongs to the caller's project. |
403 Forbidden | The user's OAuth scopes or API key permissions do not cover this endpoint. | Update the auth config scopes and have the user re-consent. For API keys, regenerate with the required permissions. |
429 Too Many Requests | Upstream rate limit (GitHub, Google, etc.). | Honor the Retry-After header from the response, back off exponentially, and batch requests where possible. Composio does not retry automatically. |
What to read next
Executing tools
Run predefined Composio tools with sessions or direct calls
Custom tools
Wrap a proxy call in a reusable custom tool with its own schema
Connected accounts
Create, list, and manage the connected accounts used by proxy execute
API reference
Full request and response schema for POST /api/v3.1/tools/execute/proxy