API Overview
Fence provides a RESTful API for integrating security scanning into your development workflow, CI/CD pipelines, and custom applications.
Availability
| Tier | API Access | Rate Limit | Features |
|---|---|---|---|
| Hobby | ❌ No | N/A | Web interface only |
| Startup | ✅ Yes | 1,000 requests/hour | Full API access, webhooks |
| Enterprise | ✅ Yes | Unlimited | Full API, custom integrations, priority support |
| Custom | ✅ Yes | Custom | White-label API, dedicated endpoints |
Authentication
Fence uses API tokens for authentication. Tokens are prefixed with fence_ for leak detection.
Creating an API Token
- Log in to Fence dashboard
- Navigate to Settings → API Tokens
- Click Create Token
- Choose token scope:
- Read-only - View domains, scans, vulnerabilities (no modifications)
- Full access - Read + write + trigger scans
- Copy token (shown once only)
- Store securely (environment variable, secrets manager)
Using Tokens
Include token in Authorization header:
curl -H "Authorization: Bearer fence_abc123..." \
https://fence.dev/api/domains/
Token security:
- Tokens expire after 90 days (automatic rotation)
- Tokens can be revoked anytime
- Leaked tokens detected via GitHub Secret Scanning
- Last used timestamp tracked for auditing
API Base URL
Production: https://fence.dev/api/
Staging: https://beta.fence.dev/api/
Response Format
All responses are JSON with consistent structure:
Success Response
{
"id": "uuid",
"name": "example.com",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-20T14:45:00Z"
}
List Response (Paginated)
{
"count": 47,
"next": "https://fence.dev/api/domains/?page=2",
"previous": null,
"results": [
{
"id": "uuid-1",
"name": "example.com"
},
{
"id": "uuid-2",
"name": "api.example.com"
}
]
}
Error Response
{
"error": "Not found",
"message": "Domain with ID 'abc123' does not exist",
"code": 404
}
Rate Limiting
API responses include rate limit headers (GitHub-style):
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 987
X-RateLimit-Reset: 1640995200
When rate limited:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640995200
Retry-After: 3600
Rate limit tiers:
- Startup: 1,000 requests/hour
- Enterprise: Unlimited
- Custom: Custom limits
Common Endpoints
Domains
# List all domains
GET /api/domains/
# Create domain
POST /api/domains/
{
"name": "example.com",
"verification_method": "dns"
}
# Get domain details
GET /api/domains/{domain_id}/
# Trigger scan
POST /api/domains/{domain_id}/scan/
Scans
# List scans
GET /api/scans/
# Get scan details
GET /api/scans/{scan_id}/
# Get scan results
GET /api/scans/{scan_id}/vulnerabilities/
Vulnerabilities
# List all vulnerabilities
GET /api/vulnerabilities/
# Get vulnerability details
GET /api/vulnerabilities/{vuln_id}/
# Mark as false positive
POST /api/vulnerabilities/{vuln_id}/false-positive/
{
"reason": "Input sanitized by WAF"
}
Webhooks
Receive real-time notifications when:
- Scan completes
- Vulnerability detected (Critical/High severity)
- Certificate expires soon
- Domain verification fails
SDKs
Official SDKs available:
| Language | Install | Documentation |
|---|---|---|
| Python | pip install fence-sdk |
https://pypi.org/project/fence-sdk |
| Node.js | npm install @fence/sdk |
https://www.npmjs.com/package/@fence/sdk |
| Go | go get github.com/fence/go-sdk |
https://pkg.go.dev/github.com/fence/go-sdk |
| Ruby | gem install fence |
https://rubygems.org/gems/fence |
Python SDK Example
from fence import FenceClient
client = FenceClient(api_token="fence_abc123...")
# List domains
domains = client.domains.list()
# Trigger scan
scan = client.domains.scan(domain_id="uuid")
# Wait for completion
scan.wait()
# Get vulnerabilities
vulnerabilities = scan.vulnerabilities()
for vuln in vulnerabilities:
print(f"{vuln.severity}: {vuln.title}")
CI/CD Integration
GitHub Actions
name: Security Scan
on: [push, pull_request]
jobs:
fence-scan:
runs-on: ubuntu-latest
steps:
- name: Trigger Fence scan
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.FENCE_API_TOKEN }}" \
-H "Content-Type: application/json" \
https://fence.dev/api/domains/${{ secrets.DOMAIN_ID }}/scan/
- name: Wait for scan completion
run: |
# Poll scan status until complete
SCAN_ID=$(curl -H "Authorization: Bearer ${{ secrets.FENCE_API_TOKEN }}" \
https://fence.dev/api/scans/latest/ | jq -r '.id')
while true; do
STATUS=$(curl -H "Authorization: Bearer ${{ secrets.FENCE_API_TOKEN }}" \
https://fence.dev/api/scans/$SCAN_ID/ | jq -r '.status')
if [ "$STATUS" = "COMPLETED" ]; then
break
fi
sleep 30
done
- name: Check for critical vulnerabilities
run: |
CRITICAL=$(curl -H "Authorization: Bearer ${{ secrets.FENCE_API_TOKEN }}" \
"https://fence.dev/api/scans/$SCAN_ID/vulnerabilities/?severity=CRITICAL" | jq '.count')
if [ "$CRITICAL" -gt 0 ]; then
echo "❌ Critical vulnerabilities detected!"
exit 1
fi
GitLab CI
fence-scan:
stage: security
script:
- |
# Trigger scan
SCAN_RESPONSE=$(curl -X POST \
-H "Authorization: Bearer $FENCE_API_TOKEN" \
https://fence.dev/api/domains/$DOMAIN_ID/scan/)
SCAN_ID=$(echo $SCAN_RESPONSE | jq -r '.id')
# Wait for completion
while true; do
STATUS=$(curl -H "Authorization: Bearer $FENCE_API_TOKEN" \
https://fence.dev/api/scans/$SCAN_ID/ | jq -r '.status')
[ "$STATUS" = "COMPLETED" ] && break
sleep 30
done
# Fail on critical vulnerabilities
CRITICAL=$(curl -H "Authorization: Bearer $FENCE_API_TOKEN" \
"https://fence.dev/api/scans/$SCAN_ID/vulnerabilities/?severity=CRITICAL" | jq '.count')
[ "$CRITICAL" -gt 0 ] && exit 1 || exit 0
only:
- main
OpenAPI Specification
Full API documentation available in OpenAPI 3.0 format:
https://fence.dev/api/schema/
https://fence.dev/api/swagger/ (Swagger UI)
https://fence.dev/api/redoc/ (ReDoc UI)
Import into Postman:
1. Open Postman
2. File → Import
3. Enter URL: https://fence.dev/api/schema/
4. Click Import
Error Codes
| Code | Meaning | Common Causes |
|---|---|---|
| 400 | Bad Request | Invalid JSON, missing required fields |
| 401 | Unauthorized | Missing or invalid API token |
| 403 | Forbidden | Token doesn't have required permissions |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Contact support |
| 503 | Service Unavailable | Scheduled maintenance |
Best Practices
Token Security
- ✅ Store tokens in environment variables
- ✅ Use secrets managers (AWS Secrets Manager, HashiCorp Vault)
- ✅ Rotate tokens every 90 days
- ✅ Use read-only tokens when possible
- ❌ Never commit tokens to Git
- ❌ Never share tokens in logs or error messages
Rate Limiting
- Use pagination for large result sets
- Implement exponential backoff when rate limited
- Cache responses when possible
- Batch operations instead of individual API calls
Error Handling
import requests
from requests.exceptions import HTTPError
try:
response = requests.post(
"https://fence.dev/api/domains/uuid/scan/",
headers={"Authorization": f"Bearer {api_token}"},
timeout=30
)
response.raise_for_status()
except HTTPError as e:
if e.response.status_code == 429:
# Rate limited - wait and retry
retry_after = int(e.response.headers.get('Retry-After', 3600))
time.sleep(retry_after)
elif e.response.status_code == 401:
# Token invalid/expired
print("API token expired - please renew")
else:
raise
Support
- API Documentation: https://fence.dev/api/docs/
- Status Page: https://status.fence.dev
- Support Email: [email protected]
- Slack Community: https://fence.dev/slack (Enterprise+)