Integrating Any API
This guide walks through integrating a real public API with netapi, start to finish. The teaching example is JSONPlaceholder — a free REST API with no auth required, which makes it ideal for learning the workflow before applying it to production APIs that need credentials.
The process has seven steps. By the end, you will have a working vendor definition and CLI commands for a new API.
Step 1: Discover
Before writing any configuration, probe the API manually with curl. The goal is to answer four questions:
-
What is the base URL?
-
What authentication does it require?
-
What resources does it expose?
-
How does it paginate?
# Probe the base URL — check if it returns something useful
curl -s https://jsonplaceholder.typicode.com | jq 'keys' 2>/dev/null || echo "Not JSON root"
# List posts (the primary resource)
curl -s https://jsonplaceholder.typicode.com/posts | jq 'length'
# => 100
# Get a single post
curl -s https://jsonplaceholder.typicode.com/posts/1 | jq .
# => { "userId": 1, "id": 1, "title": "...", "body": "..." }
# Check response headers for pagination and rate limit clues
curl -sI https://jsonplaceholder.typicode.com/posts
From this discovery:
-
Base URL:
jsonplaceholder.typicode.com -
Auth: None (public API)
-
Resources:
/posts,/comments,/users,/todos,/albums,/photos -
Pagination: None — the API returns all records in one response
For production APIs, you would also look for:
-
Linkheaders (GitHub-style pagination) -
X-RateLimit-*headers -
Required auth headers (check for
401 Unauthorizedwithout credentials) -
API documentation or OpenAPI specs
Step 2: Authenticate
JSONPlaceholder needs no authentication, but the pattern matters. When you encounter a real API, map its auth scheme to one of netapi’s universal types:
| API Says | netapi Auth Type | What You Configure |
|---|---|---|
"Pass a token in the |
|
|
"Use HTTP Basic Authentication" |
|
|
"Send your API key in the |
|
|
"Register an OAuth2 application" |
|
|
"Use mutual TLS with a client certificate" |
|
|
For JSONPlaceholder, we skip auth entirely.
The vendor definition will omit the auth block (or set type: none).
Step 3: First Call
Make the simplest possible request and parse the response:
# Fetch a single resource
curl -s https://jsonplaceholder.typicode.com/posts/1 | jq .
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae..."
}
Identify the shape:
-
ID field:
id(integer) -
Foreign keys:
userIdlinks to/users -
Content fields:
title,body
# Extract just the fields you care about
curl -s https://jsonplaceholder.typicode.com/posts/1 \
| jq '{id, title, userId}'
# List resources and extract a summary
curl -s https://jsonplaceholder.typicode.com/posts \
| jq '.[:5] | .[] | {id, title}'
Step 4: CRUD Lifecycle
Work through create, read, update, delete — verifying at each step.
Create
curl -s -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d '{
"title": "netapi test post",
"body": "Testing CRUD lifecycle",
"userId": 1
}' | jq .
{
"title": "netapi test post",
"body": "Testing CRUD lifecycle",
"userId": 1,
"id": 101
}
| JSONPlaceholder fakes mutations — it returns the expected response but does not persist changes. This is fine for learning the workflow. |
Read
# Read the resource we just "created"
curl -s https://jsonplaceholder.typicode.com/posts/101 | jq .
Update
# Full replacement (PUT)
curl -s -X PUT https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{
"id": 1,
"title": "Updated title",
"body": "Updated body",
"userId": 1
}' | jq .
# Partial update (PATCH)
curl -s -X PATCH https://jsonplaceholder.typicode.com/posts/1 \
-H "Content-Type: application/json" \
-d '{"title": "Only the title changed"}' | jq .
Step 5: Handle Edges
Production APIs throw errors, paginate, and rate-limit. Test these scenarios during discovery:
Error Responses
# Non-existent resource — expect 404
curl -s -w "\nHTTP %{http_code}" https://jsonplaceholder.typicode.com/posts/9999
# => {} with HTTP 404
# Invalid endpoint — expect 404
curl -s -w "\nHTTP %{http_code}" https://jsonplaceholder.typicode.com/nonexistent
# => HTTP 404
# Malformed request body
curl -s -X POST https://jsonplaceholder.typicode.com/posts \
-H "Content-Type: application/json" \
-d 'not json' -w "\nHTTP %{http_code}"
Pagination
JSONPlaceholder supports query parameter filtering but not true pagination. For APIs that do paginate, test the mechanics:
# Simulated: JSONPlaceholder supports _start and _limit
curl -s "https://jsonplaceholder.typicode.com/posts?_start=0&_limit=5" | jq 'length'
# => 5
curl -s "https://jsonplaceholder.typicode.com/posts?_start=5&_limit=5" | jq 'length'
# => 5
Rate Limits
# Check for rate limit headers
curl -sI https://jsonplaceholder.typicode.com/posts \
| grep -i "rate-limit"
# JSONPlaceholder has no rate limits — production APIs will
For APIs with rate limits, note:
-
Which header reports remaining requests
-
Which header reports the reset time
-
Whether they use 429 status codes
Step 6: Automate
Now translate your curl exploration into a vendor definition:
# ~/.config/netapi/vendors/jsonplaceholder.yml
name: jsonplaceholder
base_url: https://jsonplaceholder.typicode.com
auth:
type: none
pagination:
type: offset
params:
offset: _start
limit: _limit
rate_limit:
strategy: none
resources:
post:
list: GET /posts
get: GET /posts/{id}
create: POST /posts
update: PUT /posts/{id}
delete: DELETE /posts/{id}
comment:
list: GET /posts/{post_id}/comments
get: GET /comments/{id}
user:
list: GET /users
get: GET /users/{id}
todo:
list: GET /todos
get: GET /todos/{id}
Validate and test:
# Validate the YAML structure
netapi vendor validate jsonplaceholder
# Test with a live request
netapi vendor validate jsonplaceholder --live
# Use it
netapi jsonplaceholder post list --format table
netapi jsonplaceholder post get 1 --format json
netapi jsonplaceholder user list --format json | jq '.[].name'
netapi jsonplaceholder todo list --format json | jq '[.[] | select(.completed == false)] | length'
The same workflow applies to any production API.
Replace jsonplaceholder with github, cloudflare, or your internal service, fill in the auth and pagination details, and the CLI commands work identically.
Step 7: Document
Once the vendor definition works, create documentation so others can use it. A vendor’s documentation should cover:
-
Prerequisites — What credentials are needed, where to get them
-
Available resources — What you can query
-
Common workflows — The 3-5 things people actually do with this API
-
Gotchas — Rate limits, pagination quirks, required headers
For netapi’s built-in vendors, this documentation lives in the CLI reference under cli/<vendor>/.
For user-defined vendors, keep a README alongside the YAML definition or contribute it upstream.
Applying This to Production APIs
The JSONPlaceholder walkthrough covers the mechanics. Here is how the steps differ for real APIs:
| Step | Production Considerations |
|---|---|
Discover |
Start with the vendor’s API documentation. Look for OpenAPI/Swagger specs — netapi can import these directly. |
Authenticate |
Store credentials in environment variables, never in the YAML. Use dsec for secret management: |
First call |
Expect TLS certificate verification issues with self-signed certs. Use |
CRUD |
Test against a staging environment. Use |
Edges |
Map the API’s specific error format. Some return |
Automate |
Start with read-only verbs ( |
Document |
Include the output of |
Related
-
Vendor Definition Format — full YAML schema reference
-
Universal CLI Grammar — the verb vocabulary and global flags
-
Universal Patterns — vendor-agnostic concepts that apply everywhere