Overview
The Agents API provides direct execution of single-purpose agents (also called “Quick Tools”) without LLM orchestration. Each agent wraps a specific tool and returns structured results.
Key Differences from Task API:
- Agents API: Direct tool execution, no AI orchestration, synchronous results
- Task API: Multi-step workflows, LLM planning, asynchronous execution
All agent executions are asynchronous (return task_id), even though agents perform single operations. Use the Task Status API to retrieve results.
Base URL
http://localhost:8080/api/v1/agents
Production: https://api-dev.shannon.run/api/v1/agents
Authentication
Required: Yes
Include API key in header:
X-API-Key: sk_your_api_key
Development Default: Authentication is disabled when GATEWAY_SKIP_AUTH=1 is set.
Endpoints
List Agents
GET /api/v1/agents
Returns all available agents with their schemas and metadata.
Request
curl http://localhost:8080/api/v1/agents \
-H "X-API-Key: sk_test_123456"
Response
{
"agents": [
{
"id": "serp-ads",
"name": "SERP Ads Extract",
"description": "Extract paid ads from Google Search results for given keywords",
"category": "ads_research",
"tool": "ads_serp_extract",
"input_schema": {
"required": ["keywords"],
"properties": {
"keywords": {
"type": "string",
"description": "Search keywords to find ads for"
},
"country": {
"type": "string",
"default": "us",
"description": "Country code (us, jp, uk, etc.)"
}
}
},
"cost_per_call": 0.015
}
],
"count": 16
}
Response Fields
| Field | Type | Description |
|---|
agents | array | List of agent definitions |
count | integer | Total number of agents |
Agent Object:
| Field | Type | Description |
|---|
id | string | Agent identifier (used in execute endpoint) |
name | string | Human-readable name |
description | string | What the agent does |
category | string | Agent category (ads_research, financial, etc.) |
tool | string | Underlying tool name |
input_schema | object | JSON schema for input validation |
cost_per_call | number | Estimated cost per execution (USD) |
Get Agent Details
GET /api/v1/agents/
Returns details for a specific agent, including its input schema.
Request
curl http://localhost:8080/api/v1/agents/serp-ads \
-H "X-API-Key: sk_test_123456"
Response
{
"id": "serp-ads",
"name": "SERP Ads Extract",
"description": "Extract paid ads from Google Search results for given keywords",
"category": "ads_research",
"tool": "ads_serp_extract",
"input_schema": {
"required": ["keywords"],
"properties": {
"keywords": {
"type": "string",
"description": "Search keywords to find ads for"
},
"country": {
"type": "string",
"default": "us"
},
"device": {
"type": "string",
"enum": ["desktop", "mobile", "tablet"],
"default": "desktop"
}
}
},
"cost_per_call": 0.015
}
Error Responses
404 Not Found - Agent does not exist:
{
"error": "Agent not found: invalid-agent-id"
}
Execute Agent
POST /api/v1/agents/
Executes a specific agent with provided input. Returns a task ID immediately; the agent runs asynchronously.
| Header | Required | Description |
|---|
X-API-Key | Yes | API authentication key |
Content-Type | Yes | Must be application/json |
Request Body
| Parameter | Type | Required | Description |
|---|
input | object | Yes | Agent-specific input parameters (validated against input_schema) |
session_id | string | No | Session identifier for tracking (auto-generated if omitted) |
stream | boolean | No | Enable streaming (reserved for future use) |
Example: Execute SERP Ads Agent
curl -X POST http://localhost:8080/api/v1/agents/serp-ads \
-H "X-API-Key: sk_test_123456" \
-H "Content-Type: application/json" \
-d '{
"session_id": "user-session-123",
"input": {
"keywords": "best credit cards",
"country": "us",
"device": "desktop"
}
}'
Response
Status: 202 Accepted
Headers:
X-Workflow-ID: Temporal workflow identifier
X-Session-ID: Session identifier
Body:
{
"task_id": "task-abc123",
"agent_id": "serp-ads",
"status": "STATUS_CODE_OK",
"created_at": "2026-02-15T10:30:00Z"
}
Retrieve Results
Use the Get Task Status endpoint with the returned task_id:
curl http://localhost:8080/api/v1/tasks/task-abc123 \
-H "X-API-Key: sk_test_123456"
Response when completed:
{
"task_id": "task-abc123",
"status": "TASK_STATUS_COMPLETED",
"result": "{\"keywords\":\"best credit cards\",\"total_ads\":5,\"ads\":[...]}",
"response": {
"keywords": "best credit cards",
"total_ads": 5,
"ads": [
{
"position": 1,
"title": "Best Credit Cards 2026",
"description": "Compare top credit cards...",
"link": "https://example.com/cards",
"domain": "example.com"
}
],
"cost_usd": 0.015,
"timestamp": "2026-02-15T10:30:00Z"
},
"model_used": "claude-sonnet-4-20250514",
"provider": "anthropic",
"usage": {
"total_tokens": 1250,
"input_tokens": 800,
"output_tokens": 450,
"estimated_cost": 0.018
}
}
Error Responses
400 Bad Request - Invalid input:
{
"error": "input validation failed: missing required field: keywords"
}
404 Not Found - Agent does not exist:
{
"error": "Agent not found: invalid-agent"
}
429 Too Many Requests - Rate limit exceeded:
{
"error": "Rate limit exceeded"
}
All agent inputs are validated against the agent’s input_schema before execution.
Validation Rules:
- Required fields must be present and non-null
- Type checking - strings, integers, booleans, arrays, objects
- Enum validation - values must be in allowed list
- Unknown fields - rejected for security (not in schema)
Example Schema:
{
"required": ["keywords"],
"properties": {
"keywords": {
"type": "string",
"description": "Search keywords"
},
"device": {
"type": "string",
"enum": ["desktop", "mobile", "tablet"],
"default": "desktop"
},
"max_results": {
"type": "integer",
"default": 10
}
}
}
Valid input:
{
"keywords": "best shoes",
"device": "mobile",
"max_results": 5
}
Invalid input (missing required field):
Error: "input validation failed: missing required field: keywords"
Invalid input (unknown field):
{
"keywords": "shoes",
"unknown_field": "value"
}
Error: "input validation failed: unknown field: unknown_field (not defined in agent schema)"
Available Agents
Shannon provides 16+ specialized agents across multiple categories.
For a complete catalog of available agents with detailed schemas and examples, see:
Quick Reference by Category
Ads Research (10 agents):
serp-ads - Extract Google paid ads
yahoo-jp-ads - Extract Yahoo Japan sponsored ads
meta-ad-library - Search Meta Ad Library (Facebook/Instagram)
competitor-discover - Find competitor advertisers
ads-transparency - Multi-platform ad transparency data
lp-visual-analyze - Screenshot and analyze landing pages
lp-batch-analyze - Batch analyze multiple landing pages
ad-creative-analyze - Analyze ad copy patterns
keyword-extract - Extract search keywords from text
browser-screenshot - Capture webpage screenshots
Financial Tools (4 agents):
sec-filings - SEC EDGAR filings lookup
twitter-sentiment - X/Twitter sentiment via xAI
alpaca-news - Stock news from Alpaca Markets
news-aggregator - Multi-source news aggregation
Unified Task API Alternative
You can also execute agents through the unified Task API using the context.agent parameter:
curl -X POST http://localhost:8080/api/v1/tasks \
-H "X-API-Key: sk_test_123456" \
-H "Content-Type: application/json" \
-d '{
"query": "NVDA",
"session_id": "user-session-123",
"context": {
"agent": "sec-filings",
"agent_input": {
"ticker": "NVDA",
"days_back": 90
}
}
}'
Both approaches are equivalent:
- Dedicated endpoint:
POST /api/v1/agents/{id}
- Unified endpoint:
POST /api/v1/tasks with context.agent
Best Practices
Use the GET endpoint to retrieve the agent’s schema, then validate your input client-side:
import httpx
# Get schema
agent = httpx.get(
"http://localhost:8080/api/v1/agents/serp-ads",
headers={"X-API-Key": "sk_test_123456"}
).json()
# Validate required fields
required = agent["input_schema"]["required"]
input_data = {"keywords": "shoes", "device": "mobile"}
for field in required:
if field not in input_data:
raise ValueError(f"Missing required field: {field}")
2. Handle Async Results
All agents return task IDs immediately. Poll for results:
import time
# Execute agent
response = httpx.post(
"http://localhost:8080/api/v1/agents/serp-ads",
headers={"X-API-Key": "sk_test_123456"},
json={"input": {"keywords": "shoes"}}
).json()
task_id = response["task_id"]
# Poll for completion
while True:
status = httpx.get(
f"http://localhost:8080/api/v1/tasks/{task_id}",
headers={"X-API-Key": "sk_test_123456"}
).json()
if status["status"] == "TASK_STATUS_COMPLETED":
print(status["response"])
break
time.sleep(2)
3. Use Sessions for Context
Reuse session_id across related agent calls:
session_id = "user-123-analysis"
# First agent call
httpx.post(..., json={
"session_id": session_id,
"input": {"keywords": "shoes"}
})
# Second agent call (same session)
httpx.post(..., json={
"session_id": session_id,
"input": {"urls": ["https://example.com"]}
})
4. Check Cost Estimates
Before executing expensive agents, check cost_per_call:
agent = httpx.get(
"http://localhost:8080/api/v1/agents/twitter-sentiment",
headers={"X-API-Key": "sk_test_123456"}
).json()
if agent["cost_per_call"] > 0.10:
print(f"Warning: High cost agent (${agent['cost_per_call']})")
Code Examples
Python with httpx
import httpx
# List all agents
agents = httpx.get(
"http://localhost:8080/api/v1/agents",
headers={"X-API-Key": "sk_test_123456"}
).json()
print(f"Found {agents['count']} agents")
# Execute specific agent
response = httpx.post(
"http://localhost:8080/api/v1/agents/serp-ads",
headers={
"X-API-Key": "sk_test_123456",
"Content-Type": "application/json"
},
json={
"session_id": "my-session",
"input": {
"keywords": "running shoes",
"country": "us",
"device": "mobile"
}
}
).json()
print(f"Task ID: {response['task_id']}")
JavaScript/Node.js
const axios = require('axios');
// Get agent details
const agent = await axios.get(
'http://localhost:8080/api/v1/agents/serp-ads',
{
headers: { 'X-API-Key': 'sk_test_123456' }
}
);
console.log('Agent schema:', agent.data.input_schema);
// Execute agent
const response = await axios.post(
'http://localhost:8080/api/v1/agents/serp-ads',
{
session_id: 'my-session',
input: {
keywords: 'running shoes',
country: 'us'
}
},
{
headers: {
'X-API-Key': 'sk_test_123456',
'Content-Type': 'application/json'
}
}
);
console.log('Task ID:', response.data.task_id);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type AgentExecuteRequest struct {
SessionID string `json:"session_id,omitempty"`
Input map[string]interface{} `json:"input"`
}
func executeAgent(agentID string, input map[string]interface{}) (string, error) {
req := AgentExecuteRequest{
SessionID: "my-session",
Input: input,
}
body, _ := json.Marshal(req)
httpReq, _ := http.NewRequest(
"POST",
fmt.Sprintf("http://localhost:8080/api/v1/agents/%s", agentID),
bytes.NewBuffer(body),
)
httpReq.Header.Set("X-API-Key", "sk_test_123456")
httpReq.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result map[string]interface{}
json.NewDecoder(resp.Body).Decode(&result)
return result["task_id"].(string), nil
}
func main() {
taskID, _ := executeAgent("serp-ads", map[string]interface{}{
"keywords": "running shoes",
"country": "us",
})
fmt.Printf("Task ID: %s\n", taskID)
}