Overview
Vercel exposes a REST API and CLI for full programmatic control of deployments, environment variables, domains, and edge config. Use the CLI for interactive ops; use the REST API for automation.
Authentication
# CLI login
vercel login
# REST API — Bearer token (generate at vercel.com/account/tokens)
Authorization: Bearer <token>
Core CLI Commands
# Deploy current directory
vercel # Preview deployment
vercel --prod # Production deployment
# Project management
vercel ls # List projects
vercel inspect <url> # Deployment details
vercel logs <url> # Stream logs
# Environment variables
vercel env add NAME production # Add env var
vercel env ls # List all env vars
vercel env rm NAME production # Remove env var
vercel env pull .env.local # Pull env vars to local file
# Domains
vercel domains add example.com # Add domain
vercel domains ls # List domains
# Rollback
vercel rollback [deployment-url] # Rollback to previous
Environment Variables API
// List env vars for a project
const vars = await fetch(
"https://api.vercel.com/v9/projects/{projectId}/env",
{ headers: { Authorization: `Bearer ${token}` } }
).then(r => r.json());
// Create env var
await fetch("https://api.vercel.com/v10/projects/{projectId}/env", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
key: "MY_API_KEY",
value: "secret-value",
type: "encrypted", // "plain" | "encrypted" | "secret"
target: ["production", "preview", "development"],
}),
});
Deployments API
// List deployments
const deploys = await fetch(
"https://api.vercel.com/v6/deployments?projectId={id}&limit=20",
{ headers: { Authorization: `Bearer ${token}` } }
).then(r => r.json());
// Trigger a deployment (from Git)
await fetch("https://api.vercel.com/v13/deployments", {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({
name: "my-project",
gitSource: {
type: "github",
repoId: "123456",
ref: "main",
},
target: "production",
}),
});
// Get deployment status
const deploy = await fetch(
"https://api.vercel.com/v13/deployments/{deploymentId}",
{ headers: { Authorization: `Bearer ${token}` } }
).then(r => r.json());
// deploy.readyState: "QUEUED" | "BUILDING" | "READY" | "ERROR"
vercel.json Configuration
{
"buildCommand": "npm run build",
"outputDirectory": ".next",
"installCommand": "npm ci",
"framework": "nextjs",
"regions": ["iad1"],
"headers": [
{
"source": "/api/(.*)",
"headers": [{ "key": "Cache-Control", "value": "no-store" }]
}
],
"rewrites": [
{ "source": "/old-path", "destination": "/new-path" }
],
"redirects": [
{ "source": "/old", "destination": "/new", "permanent": true }
],
"crons": [
{ "path": "/api/cron/daily", "schedule": "0 8 * * *" }
]
}
Domains API
// Add domain to project
await fetch("https://api.vercel.com/v10/projects/{projectId}/domains", {
method: "POST",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({ name: "example.com" }),
});
// Verify domain
const check = await fetch(
"https://api.vercel.com/v6/domains/{domain}/config",
{ headers: { Authorization: `Bearer ${token}` } }
).then(r => r.json());
// check.misconfigured: bool — true means DNS not yet propagated
GitHub Actions Integration
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
npm install -g vercel
vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Edge Config
Low-latency key-value store read at the edge before your function runs:
import { get } from "@vercel/edge-config";
export async function middleware(request: NextRequest) {
const maintenanceMode = await get<boolean>("maintenanceMode");
if (maintenanceMode) {
return NextResponse.rewrite(new URL("/maintenance", request.url));
}
}
Update via REST API — changes propagate in milliseconds globally.