Developers · early access
InternetSecure API
Read your monitors and run security scans from scripts, CI pipelines and AI assistants. Every account gets API keys, free included; plans differ in limits.
Quick start
- Create a key in Settings → API keys. It's shown once.
- Check it works, then scan a site and fail a deploy below a grade:
export ISEC_KEY=isk_...
curl https://internetsecure.org/api/v1/me -H "Authorization: Bearer $ISEC_KEY"
curl -X POST https://internetsecure.org/api/v1/scans \
-H "Authorization: Bearer $ISEC_KEY" \
-d '{"domain": "example.com", "min_grade": "B"}'
# → "gate": {"min_grade": "B", "passed": true}
Use it from an AI assistant (MCP)
The MCP server at https://internetsecure.org/mcp gives Claude, Cursor and VS Code eight tools: get_account, list_monitors, get_monitor, get_security_result, run_scan, get_scan, add_monitor and update_monitor. It uses the same key, limits and scan quota as the API. With a read-only key it can only look and scan; with a full-access key it can add and change monitors (your assistant asks you first, and every change can be undone in the Activity Log). Removing monitors and connecting Slack, Teams, JIRA or Linear stay on the website.
Claude Code Run in a terminal, then start a new claude session:
claude mcp add --transport http internetsecure https://internetsecure.org/mcp \ --header "Authorization: Bearer isk_YOUR_KEY"
Cursor Add to ~/.cursor/mcp.json:
{
"mcpServers": {
"internetsecure": {
"url": "https://internetsecure.org/mcp",
"headers": {
"Authorization": "Bearer isk_YOUR_KEY"
}
}
}
}
VS Code Add to .vscode/mcp.json:
{
"servers": {
"internetsecure": {
"type": "http",
"url": "https://internetsecure.org/mcp",
"headers": {
"Authorization": "Bearer isk_YOUR_KEY"
}
}
}
}
Claude Desktop Settings → Developer → Edit Config, add (needs Node.js), then restart Claude:
{
"mcpServers": {
"internetsecure": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://internetsecure.org/mcp",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer isk_YOUR_KEY"
}
}
}
}
Auth
Every call sends Authorization: Bearer isk_…. Create keys in Settings → API keys on any plan, up to 10 per account (e.g. "GitHub Actions", "Claude"). A key is shown once and stored hashed; revoke it any time. Tick Read-only for keys that only read and scan, such as one for an AI assistant. There is no anonymous access.
Limits
| Plan | Requests/min | Scans/day |
|---|---|---|
| Free | 60 | 10 |
| Plus | 120 | 100 |
| Pro | 300 | 500 |
| Enterprise | 600 | 2,000 |
Only POST /scans uses scan quota. Failed scans don't count. Headers: X-RateLimit-Remaining, X-Scans-Remaining, Retry-After.
Errors
Always the same shape, with a plain message an AI can repeat to the user:
{
"error": {
"code": "scan_quota",
"message": "Used 10 of 10 scans today on Free. Resets 2026-11-07T00:00:00Z.",
"docs": "https://internetsecure.org/docs/api#errors"
}
}
Account
Who the key belongs to, the plan, and what is left of today's limits.
get
/me
Account, plan and remaining limits
Handy first call for an AI assistant: tells it what it can do before it tries.
Responses
- 200 The account behind this key.
- 401 Missing, wrong or revoked key.
unauthorized
Example
curl https://internetsecure.org/api/v1/me \ -H "Authorization: Bearer $ISEC_KEY"
Response 200
{
"account": {
"id": 1042,
"email": "[email protected]",
"plan": "pro"
},
"limits": {
"monitors": {
"used": 37,
"max": 100
},
"requests_per_minute": 300,
"scans_per_day": {
"used": 12,
"max": 500,
"resets_at": "2026-11-07T00:00:00Z"
}
},
"key": {
"name": "GitHub Actions",
"read_only": false,
"created_at": "2026-11-01T09:00:00Z",
"last_used_at": "2026-11-06T17:44:58Z"
},
"integrations": {
"email": {
"connected": true,
"enabled": true,
"settings_url": "https://internetsecure.org/account/profile?panel=email"
},
"slack": {
"connected": true,
"uptime_alerts": true,
"security_alerts": true,
"settings_url": "https://internetsecure.org/account/profile?panel=slack"
},
"teams": {
"connected": false,
"uptime_alerts": false,
"security_alerts": false,
"settings_url": "https://internetsecure.org/account/profile?panel=teams"
},
"jira": {
"connected": false,
"uptime_alerts": false,
"security_alerts": false,
"settings_url": "https://internetsecure.org/account/profile?panel=jira"
},
"linear": {
"connected": false,
"uptime_alerts": false,
"security_alerts": false,
"settings_url": "https://internetsecure.org/account/profile?panel=linear"
}
}
}
Monitors
The domains on your account: uptime checks and scheduled security scans. Same plan limits as the website.
get
/monitors
List monitors
Parameters
statusquery Only monitors in this uptime state.searchquery Part of a domain name.limitquerycursorquery next_cursor from the previous page.
Responses
- 200 A page of monitors.
- 401 Missing, wrong or revoked key.
unauthorized - 429 Too many requests this minute.
rate_limited
Example
curl https://internetsecure.org/api/v1/monitors \ -H "Authorization: Bearer $ISEC_KEY"
Response 200
{
"data": [
{
"id": 48213,
"domain": "example.com",
"uptime": {
"enabled": true,
"status": "up",
"last_checked_at": "2026-11-06T17:42:10Z",
"response_ms": 212,
"uptime_24h": 100,
"uptime_30d": 99.97,
"open_incident": null
},
"security": {
"enabled": true,
"schedule": "daily",
"live": true,
"grade": "B",
"exposure_grade": "A",
"last_scan_at": "2026-11-06T06:03:44Z"
},
"report_url": "https://internetsecure.org/account/uptime/48213"
}
],
"next_cursor": null
}
post
/monitors
Add a monitor
Adds a domain to the account, exactly like Monitors → Add on the website. New domains get daily security scans; paid plans also get the 2-hour live exposure check.
Responses
- 201 Created.
- 401 Missing, wrong or revoked key.
unauthorized - 403 Plan limit reached (plan_limit), or the key is read-only (read_only_key).
plan_limit - 409 Already on the account.
already_exists - 422 Not a public domain.
invalid_domain - 429 Too many requests this minute.
rate_limited
Example
curl -X POST https://internetsecure.org/api/v1/monitors \
-H "Authorization: Bearer $ISEC_KEY" \
-d '{"domain":"example.com","uptime":true,"security_scans":true}'
Response 201
{
"id": 48213,
"domain": "example.com",
"uptime": {
"enabled": true,
"status": "up",
"last_checked_at": "2026-11-06T17:42:10Z",
"response_ms": 212,
"uptime_24h": 100,
"uptime_30d": 99.97,
"open_incident": null
},
"security": {
"enabled": true,
"schedule": "daily",
"live": true,
"grade": "B",
"exposure_grade": "A",
"last_scan_at": "2026-11-06T06:03:44Z"
},
"report_url": "https://internetsecure.org/account/uptime/48213"
}
get
/monitors/{id}
Get one monitor
Parameters
idpath Monitor id (from GET /monitors).
Responses
- 200 The monitor.
- 404 No such id on this account.
not_found
Example
curl https://internetsecure.org/api/v1/monitors/48213 \ -H "Authorization: Bearer $ISEC_KEY"
Response 200
{
"id": 48213,
"domain": "example.com",
"uptime": {
"enabled": true,
"status": "up",
"last_checked_at": "2026-11-06T17:42:10Z",
"response_ms": 212,
"uptime_24h": 100,
"uptime_30d": 99.97,
"open_incident": null
},
"security": {
"enabled": true,
"schedule": "daily",
"live": true,
"grade": "B",
"exposure_grade": "A",
"last_scan_at": "2026-11-06T06:03:44Z"
},
"report_url": "https://internetsecure.org/account/uptime/48213"
}
patch
/monitors/{id}
Change or pause a monitor
Send only the fields you want to change. To pause uptime checks during a deploy: {"uptime": false}, then {"uptime": true} after. Pausing keeps the check and its history. alerts applies to every check on the monitor. Every change shows in the account's Activity Log, where it can be undone. Monitors can only be removed on the website.
Parameters
idpath Monitor id (from GET /monitors).
Responses
- 200 Updated.
- 403 The key is read-only.
read_only_key - 404 No such id on this account.
not_found - 422 Bad field.
invalid_field
Example
curl -X PATCH https://internetsecure.org/api/v1/monitors/48213 \
-H "Authorization: Bearer $ISEC_KEY" \
-d '{"uptime":false}'
Response 200
{
"id": 48213,
"domain": "example.com",
"uptime": {
"enabled": false,
"status": "paused",
"last_checked_at": "2026-11-06T17:42:10Z",
"response_ms": 212,
"uptime_24h": 100,
"uptime_30d": 99.97,
"open_incident": null
},
"security": {
"enabled": true,
"schedule": "daily",
"live": true,
"grade": "B",
"exposure_grade": "A",
"last_scan_at": "2026-11-06T06:03:44Z"
},
"report_url": "https://internetsecure.org/account/uptime/48213"
}
get
/monitors/{id}/security
Latest security result for a monitor
The most recent scheduled or live scan, with every check. Does not run a new scan and does not use scan quota.
Parameters
idpath Monitor id (from GET /monitors).
Responses
- 200 Latest result.
- 404 No such monitor on this account, or it has no scan yet (code not_scanned).
not_found
Example
curl https://internetsecure.org/api/v1/monitors/48213/security \ -H "Authorization: Bearer $ISEC_KEY"
Response 200
{
"id": 9918231,
"domain": "example.com",
"scanned_at": "2026-11-06T17:45:02Z",
"duration_ms": 3810,
"security": {
"grade": "B",
"score": 78,
"passed": 8,
"warnings": 3,
"failed": 1
},
"exposure": {
"grade": "A",
"findings": []
},
"checks": [
{
"id": "ssl",
"name": "SSL certificate",
"status": "pass",
"detail": "Valid, expires 2027-01-15 (70 days)"
},
{
"id": "mixed_content",
"name": "Mixed content",
"status": "pass",
"detail": "None found"
},
{
"id": "hsts",
"name": "HSTS",
"status": "pass",
"detail": "max-age=31536000"
},
{
"id": "x_frame_options",
"name": "X-Frame-Options",
"status": "fail",
"detail": "Missing"
},
{
"id": "csp",
"name": "Content-Security-Policy",
"status": "warn",
"detail": "Missing"
},
{
"id": "tls",
"name": "TLS",
"status": "pass",
"detail": "TLS 1.3, strong cipher"
},
{
"id": "security_headers",
"name": "Security headers",
"status": "pass",
"detail": "4 of 6 present"
},
{
"id": "cookies",
"name": "Cookies",
"status": "pass",
"detail": "Secure and HttpOnly"
},
{
"id": "dmarc",
"name": "DMARC",
"status": "pass",
"detail": "p=reject"
},
{
"id": "dkim",
"name": "DKIM",
"status": "warn",
"detail": "No record on common selectors"
},
{
"id": "dnssec",
"name": "DNSSEC",
"status": "warn",
"detail": "Not enabled"
},
{
"id": "server_version",
"name": "Server version",
"status": "pass",
"detail": "Not announced"
},
{
"id": "waf",
"name": "WAF",
"status": "info",
"detail": "None announced (not scored)"
}
],
"previous": {
"scan_id": 9917004,
"grade": "A",
"exposure_grade": "A",
"scanned_at": "2026-11-06T06:02:11Z"
},
"changes": [
{
"check": "csp",
"name": "Content-Security-Policy",
"from": "pass",
"to": "warn",
"worse": true
}
],
"report_url": "https://internetsecure.org/account/scan/9918231",
"monitor_id": 48213
}
Scans
Run a security scan now, e.g. as a deploy check in CI. Counts against the daily scan quota.
post
/scans
Run a security scan now
Scans any public domain (it does not have to be a monitor) and waits for the result, usually 2–10 seconds. Uses 1 from the daily scan quota. For CI: pass min_grade and/or no_drop and check gate.passed (fail the build when false); gate.reason is a one-line explanation for the build log. Every result also says what changed since your previous scan of the domain.
Responses
- 200 Scan finished.
- 401 Missing, wrong or revoked key.
unauthorized - 422 Not a public domain.
invalid_domain - 429 Daily scan quota used up (or too many requests).
scan_quota - 502 The site could not be scanned (no answer, DNS failure).
scan_failed - 503 Scanners are busy; retry after the Retry-After header.
busy
Example
curl -X POST https://internetsecure.org/api/v1/scans \
-H "Authorization: Bearer $ISEC_KEY" \
-d '{"domain":"example.com","min_grade":"B"}'
Response 200
{
"id": 9918231,
"domain": "example.com",
"scanned_at": "2026-11-06T17:45:02Z",
"duration_ms": 3810,
"security": {
"grade": "B",
"score": 78,
"passed": 8,
"warnings": 3,
"failed": 1
},
"exposure": {
"grade": "A",
"findings": []
},
"checks": [
{
"id": "ssl",
"name": "SSL certificate",
"status": "pass",
"detail": "Valid, expires 2027-01-15 (70 days)"
},
{
"id": "mixed_content",
"name": "Mixed content",
"status": "pass",
"detail": "None found"
},
{
"id": "hsts",
"name": "HSTS",
"status": "pass",
"detail": "max-age=31536000"
},
{
"id": "x_frame_options",
"name": "X-Frame-Options",
"status": "fail",
"detail": "Missing"
},
{
"id": "csp",
"name": "Content-Security-Policy",
"status": "warn",
"detail": "Missing"
},
{
"id": "tls",
"name": "TLS",
"status": "pass",
"detail": "TLS 1.3, strong cipher"
},
{
"id": "security_headers",
"name": "Security headers",
"status": "pass",
"detail": "4 of 6 present"
},
{
"id": "cookies",
"name": "Cookies",
"status": "pass",
"detail": "Secure and HttpOnly"
},
{
"id": "dmarc",
"name": "DMARC",
"status": "pass",
"detail": "p=reject"
},
{
"id": "dkim",
"name": "DKIM",
"status": "warn",
"detail": "No record on common selectors"
},
{
"id": "dnssec",
"name": "DNSSEC",
"status": "warn",
"detail": "Not enabled"
},
{
"id": "server_version",
"name": "Server version",
"status": "pass",
"detail": "Not announced"
},
{
"id": "waf",
"name": "WAF",
"status": "info",
"detail": "None announced (not scored)"
}
],
"previous": {
"scan_id": 9917004,
"grade": "A",
"exposure_grade": "A",
"scanned_at": "2026-11-06T06:02:11Z"
},
"changes": [
{
"check": "csp",
"name": "Content-Security-Policy",
"from": "pass",
"to": "warn",
"worse": true
}
],
"gate": {
"min_grade": "B",
"no_drop": true,
"passed": false,
"reason": "Grade dropped A → B: Content-Security-Policy pass → warn"
},
"report_url": "https://internetsecure.org/account/scan/9918231"
}
get
/scans/{id}
Fetch an earlier scan
Only scans run by this account.
Parameters
idpath Scan id from POST /scans.
Responses
- 200 The scan.
- 404 No such id on this account.
not_found
Example
curl https://internetsecure.org/api/v1/scans/9918231 \ -H "Authorization: Bearer $ISEC_KEY"
Response 200
{
"id": 9918231,
"domain": "example.com",
"scanned_at": "2026-11-06T17:45:02Z",
"duration_ms": 3810,
"security": {
"grade": "B",
"score": 78,
"passed": 8,
"warnings": 3,
"failed": 1
},
"exposure": {
"grade": "A",
"findings": []
},
"checks": [
{
"id": "ssl",
"name": "SSL certificate",
"status": "pass",
"detail": "Valid, expires 2027-01-15 (70 days)"
},
{
"id": "mixed_content",
"name": "Mixed content",
"status": "pass",
"detail": "None found"
},
{
"id": "hsts",
"name": "HSTS",
"status": "pass",
"detail": "max-age=31536000"
},
{
"id": "x_frame_options",
"name": "X-Frame-Options",
"status": "fail",
"detail": "Missing"
},
{
"id": "csp",
"name": "Content-Security-Policy",
"status": "warn",
"detail": "Missing"
},
{
"id": "tls",
"name": "TLS",
"status": "pass",
"detail": "TLS 1.3, strong cipher"
},
{
"id": "security_headers",
"name": "Security headers",
"status": "pass",
"detail": "4 of 6 present"
},
{
"id": "cookies",
"name": "Cookies",
"status": "pass",
"detail": "Secure and HttpOnly"
},
{
"id": "dmarc",
"name": "DMARC",
"status": "pass",
"detail": "p=reject"
},
{
"id": "dkim",
"name": "DKIM",
"status": "warn",
"detail": "No record on common selectors"
},
{
"id": "dnssec",
"name": "DNSSEC",
"status": "warn",
"detail": "Not enabled"
},
{
"id": "server_version",
"name": "Server version",
"status": "pass",
"detail": "Not announced"
},
{
"id": "waf",
"name": "WAF",
"status": "info",
"detail": "None announced (not scored)"
}
],
"previous": {
"scan_id": 9917004,
"grade": "A",
"exposure_grade": "A",
"scanned_at": "2026-11-06T06:02:11Z"
},
"changes": [
{
"check": "csp",
"name": "Content-Security-Policy",
"from": "pass",
"to": "warn",
"worse": true
}
],
"report_url": "https://internetsecure.org/account/scan/9918231"
}