Monitors are the core resource in Oh Dear. Each monitor tracks a URL or hostname and runs checks against it. You can monitor websites (HTTP), servers (ping), TCP ports, and use AI to verify page content.
You'll need a team_id to create monitors -- see the user info endpoint to find your teams.
Example request:
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/99 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
All endpoints below follow the same authentication pattern.
Get a specific monitor #
GET /api/monitors/{monitorId}
Retrieve a single monitor by its ID.
The resulting response is a single monitor object:
{
"id": 99,
"team_id": 1,
"type": "http",
"url": "https://example.com",
"uses_https": true,
"sort_url": "example.com",
"label": "example.com",
"group_name": "",
"tags": [],
"description": null,
"notes": null,
"real_ip_address": null,
"latest_run_date": "2019-09-16 07:29:02",
"summarized_check_result": "succeeded",
"checks": [
{
"id": 100,
"type": "uptime",
"label": "Uptime",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:02",
"latest_run_result": "succeeded",
"summary": "Up",
"settings": {
"location": "paris",
"look_for_string": "",
"absent_string": null,
"expected_response_headers": [],
"failed_notification_threshold": 2,
"http_verb": "get",
"payload": [],
"timeout": 5,
"valid_status_codes": ["2*"],
"max_redirect_count": 5,
"expected_final_redirect_url": null,
"http_client_headers": []
},
"active_snooze": null
}
],
"uptime_check_settings": {
"location": "paris",
"look_for_string": "",
"absent_string": null,
"expected_response_headers": [],
"failed_notification_threshold": 2,
"http_verb": "get",
"payload": [],
"timeout": 5,
"valid_status_codes": ["2*"],
"max_redirect_count": 5,
"expected_final_redirect_url": null,
"http_client_headers": []
},
"certificate_health_check_settings": {
"expires_soon_threshold_in_days": null
},
"broken_links_check_settings": {
"whitelisted_urls": [],
"check_include_external_links": false,
"types": ["link", "image", "script", "stylesheet", "og:image"],
"do_not_crawl_urls": [],
"force_crawl_urls": [],
"new_links_only": false
},
"dns_check_settings": {
"extra_cnames": [],
"monitor_main_domain": false,
"ignored_record_types": ["SOA"],
"check_nameservers_in_sync": true
},
"lighthouse_check_settings": {
"continent": "europe",
"device_emulation": "desktop",
"notification_settings": null,
"http_client_headers": []
},
"application_health_check_settings": {
"result_url": null,
"secret": "your-secret-key",
"headers": []
},
"domain_check_settings": {
"not_supported_reason": null,
"expires_soon_threshold_in_days": null
},
"performance_check_settings": {
"threshold_in_ms": 3500,
"change_percentage": 50,
"failed_notification_threshold": 0
},
"sitemap_check_settings": {
"path": "sitemap.xml",
"speed": "default"
},
"ports_check_settings": {
"expected_open_ports": [80, 443],
"failed_notification_threshold": 0,
"continent": "europe",
"baseline_confirmed": true,
"behind_cloudflare": false
},
"dns_blocklist_check_settings": {
"use_all_blocklists": true,
"enabled_blocklists": []
},
"ai_check_settings": null,
"crawler_headers": [],
"send_report_to_emails": [],
"include_check_types_in_report": [],
"badge_id": "badge-123",
"marked_for_deletion_at": null,
"created_at": "2019-09-16 07:25:00",
"updated_at": "2019-09-16 07:25:00"
}
Get a specific monitor by URL #
GET /api/monitors/url/{monitorUrl}
Retrieve a monitor by its exact URL. The value should be URL-encoded.
Returns the same monitor object as the ID-based endpoint.
Get all monitors in your account #
GET /api/monitors
Lists all your monitors. Each item follows the same shape as the single monitor response above.
Notice that the uptime_check_settings shape depends on the monitor type. Here's a response showing all three types:
{
"data": [
{
"id": 1,
"type": "http",
"url": "https://freek.dev",
"summarized_check_result": "succeeded",
"uptime_check_settings": {
"location": "paris",
"http_verb": "get",
"timeout": 5,
"valid_status_codes": ["2*"],
"..."
},
"..."
},
{
"id": 2,
"type": "ping",
"url": "spatie.be",
"summarized_check_result": "succeeded",
"uptime_check_settings": {
"location": "paris",
"ttl": 64,
"count": 5,
"packet_size_in_bytes": 56,
"acceptable_packet_loss_percentage": 0,
"acceptable_average_response_time_in_ms": 400,
"..."
},
"..."
},
{
"id": 3,
"type": "tcp",
"url": "smtp.gmail.com:3306",
"summarized_check_result": "succeeded",
"uptime_check_settings": {
"location": "paris",
"tcp_send_string": "",
"timeout_in_ms": 1000,
"look_for_string_in_welcome_message": "",
"..."
},
"..."
}
],
"links": { "..." },
"meta": { "..." }
}
The three monitor types are:
- HTTP Monitor (
type: "http"): Monitors websites with comprehensive checks including uptime, performance, certificate health, broken links, mixed content, lighthouse, DNS, domain expiration, and more.
- Ping Monitor (
type: "ping"): Monitors server availability using ICMP ping. Only has uptime checks with different settings (TTL, packet size, packet loss percentage, etc.).
- TCP Port Monitor (
type: "tcp"): Monitors specific TCP port availability (like MySQL, SMTP). Has uptime checks with TCP-specific settings like connection strings and timeouts.
Each monitor type has different available checks and settings. More on each check in our dedicated checks page.
Monitor properties #
The response gives you comprehensive details for each monitor. Here are the properties:
Core properties #
id: Unique internal identifier for the monitor
team_id: Every monitor belongs to a Team. This is the team's ID. Since you can belong to multiple teams, the corresponding team ID will be shown for every monitor.
type: The monitor type -- "http" for websites, "ping" for ICMP ping monitoring, "tcp" for TCP port monitoring, or "ai" for AI verification monitors
url: The URL/hostname you submitted to Oh Dear (includes protocol for HTTP monitors, hostname for ping, hostname:port for TCP, and the target URL for AI monitors)
uses_https: Boolean indicating whether it uses HTTPS (mainly relevant for HTTP monitors)
sort_url: The URL without protocol and www. prefix, used for sorting
label: Display name for the monitor (defaults to sort_url if no custom name provided)
group_name: Name of the group this monitor belongs to (empty string if ungrouped)
tags: Array of tag names associated with this monitor
description: Optional description for the monitor
notes: Custom notes for the monitor
real_ip_address: The origin server's IP address. If your site is behind a CDN or WAF, set this so checks like DNS blocklist use your actual server IP instead of the CDN's resolved IPs.
latest_run_date: Timestamp (UTC) of when any check for this monitor last ran
summarized_check_result: Overall monitor health. Possible values: "succeeded", "warning", "failed", "pending" -- see summarized check results
Checks array #
checks: Array of all checks configured for this monitor. Each check includes:
id: Unique identifier for the check
type: Check type (uptime, performance, certificate_health, broken_links, mixed_content, lighthouse, cron, application_health, sitemap, dns, domain, ai, ports, dns_blocklist)
label: Human-readable label for the check
enabled: Whether this check is currently active
latest_run_ended_at: When this specific check last completed (UTC)
latest_run_result: Result of the last run (succeeded, failed, warning, pending, errored-or-timed-out)
summary: Human-readable summary of the current status
settings: Check-specific configuration (varies by check type)
active_snooze: Information about any active notification snoozing
Monitor type-specific settings #
Depending on the monitor type, different settings objects are included:
HTTP Monitors (type: "http"):
uptime_check_settings: HTTP-specific uptime settings
location: Server location for checks (e.g., "paris")
look_for_string: Text that must be present in response
absent_string: Text that must NOT be present in response
expected_response_headers: Headers that must be present (with conditions)
failed_notification_threshold: How many failures before alerting
http_verb: HTTP method to use ("get", "post", etc.)
payload: Request body for POST/PUT requests
timeout: Request timeout in seconds
valid_status_codes: Array of acceptable HTTP status codes (e.g., ["2*", "3*"])
max_redirect_count: Maximum redirects to follow
expected_final_redirect_url: Expected final URL after redirects
http_client_headers: Custom headers to send with requests
Ping Monitors (type: "ping"):
uptime_check_settings: ICMP ping settings
location: Server location for checks
ttl: Time-to-live for ping packets
count: Number of ping packets to send
packet_size_in_bytes: Size of each ping packet
acceptable_packet_loss_percentage: Maximum acceptable packet loss
acceptable_average_response_time_in_ms: Maximum acceptable average response time
timeout_in_seconds: Timeout for each ping
interval_in_seconds: Interval between pings
failed_notification_threshold: How many failures before alerting
TCP Monitors (type: "tcp"):
uptime_check_settings: TCP connection settings
location: Server location for checks
tcp_send_string: String to send after connecting
timeout_in_ms: Connection timeout in milliseconds
look_for_string_in_welcome_message: Text to look for in initial server response
look_for_string_in_send_string_response: Text to look for in response to sent string
failed_notification_threshold: How many failures before alerting
Check-specific settings objects #
All monitors include these settings objects (even if the checks are disabled):
certificate_health_check_settings: SSL certificate monitoring
expires_soon_threshold_in_days: Days before expiration to warn
broken_links_check_settings: Broken link detection
whitelisted_urls: URLs to ignore if broken
check_include_external_links: Whether to check external links
types: Link types to check (["link", "image", "script", "stylesheet", "og:image"])
do_not_crawl_urls: URLs to exclude from crawling (currently returned in responses; not accepted in create/update payload validation)
force_crawl_urls: URLs to always crawl
new_links_only: Only check newly discovered links
dns_check_settings: DNS monitoring configuration
extra_cnames: Additional CNAME records to monitor
monitor_main_domain: Whether to monitor the main domain
ignored_record_types: DNS record types to ignore (e.g., ["SOA"])
check_nameservers_in_sync: Whether to verify nameservers are synchronized
lighthouse_check_settings: Performance audit configuration
continent: Geographic region for testing ("europe", "north-america", "asia")
device_emulation: Device to emulate ("desktop" or "mobile")
notification_settings: Custom notification thresholds
http_client_headers: Custom headers for the audit
application_health_check_settings: Custom application health endpoint
result_url: URL of your health check endpoint
secret: Secret token for authentication
headers: Custom headers to send
domain_check_settings: Domain expiration monitoring
not_supported_reason: Why domain monitoring isn't available (if applicable)
expires_soon_threshold_in_days: Days before expiration to warn
performance_check_settings: Response time monitoring
threshold_in_ms: Maximum acceptable response time
change_percentage: Percentage change threshold for alerts
failed_notification_threshold: Consecutive slow checks before the first alert is sent (0 = alert on the first slow check)
sitemap_check_settings: Sitemap validation
path: Path to sitemap file (e.g., "sitemap.xml")
speed: Crawl speed ("default", "fast", "slow")
ports_check_settings: Port availability monitoring
expected_open_ports: Array of ports that should be open (max 20, values 1-65535)
failed_notification_threshold: Consecutive failed scans before the first notification is sent (0 = notify on the first failure)
continent: Geographic region the port scan runs from ("europe", "north-america", "asia")
baseline_confirmed: Read-only. true once you've confirmed which ports you expect to be open. Until then the check stays in its onboarding phase and does not send alerts.
behind_cloudflare: Read-only. true when the monitored host resolves to a Cloudflare IP and can't be scanned; add an origin IP address under the monitor's general settings so we can scan the real server.
dns_blocklist_check_settings: DNS blocklist monitoring
use_all_blocklists: Whether to check all available blocklists (default: true)
enabled_blocklists: Array of specific blocklist names to check (only used when use_all_blocklists is false)
ai_check_settings: AI verification monitoring (only for type: "ai" monitors)
prompt: The AI prompt used to verify the page content
frequency_unit / frequency_value: How often the AI check runs
suppress_repeated_success_notifications: Whether repeated success notifications are suppressed
throttle_failed_interval_unit / throttle_failed_interval_value: Failure notification throttling interval
failed_notification_threshold: Consecutive failed checks before the first notification is sent (0 = notify on the first failure)
html_stripping_level: How much HTML to strip before sending to AI ("basic", "aggressive", or "none")
location and wizard_* fields may also be present in responses
Additional properties #
crawler_headers: Custom headers sent during crawling operations
send_report_to_emails: Email addresses that receive the monthly reports
include_check_types_in_report: Which check types are included in monthly reports
badge_id: Unique identifier for status badge integration
marked_for_deletion_at: Timestamp (UTC) if monitor is scheduled for deletion
created_at: When the monitor was created (UTC)
updated_at: When the monitor was last modified (UTC)
Add a monitor through the API #
POST /api/monitors
Create a new monitor and start monitoring immediately.
Request body (JSON):
url (string, required) -- the URL/hostname to monitor
team_id (integer, required) -- the team this monitor belongs to. See user info to find your teams.
type (string, required) -- "http" for websites, "ping" for ICMP ping, "tcp" for TCP port, or "ai" for AI verification
checks (array, optional) -- array of check types to enable. If omitted, the default checks for the monitor type will be enabled.
{
"url": "https://example.com",
"team_id": "1",
"type": "http"
}
Returns a monitor object with all checks set to "pending".
Add a monitor with enabled checks #
When creating a monitor you can control which checks get enabled by passing an array of check types.
Here's an example payload where only the uptime and mixed_content checks are enabled:
{
"url": "https://mybrandnewsite.tld",
"team_id": "1",
"type": "http",
"checks": ["uptime", "mixed_content"]
}
For more details on each check, have a look at the checks API endpoint.
Add a monitor with custom settings #
You can pass additional parameters to control the settings of a monitor. You can also control the monthly report settings using send_report_to_emails and include_check_types_in_report.
Please be aware:
application_health_check_settings.secret should not be present if you want to generate a random secret
- default values will be used for any settings not present in the payload
- we suggest only adding settings you want to change from the defaults
Here's the full payload with all possible settings:
{
"url": "https://ohdear.app/docs",
"team_id": 1,
"type": "http",
"checks": [
"uptime",
"performance",
"broken_links",
"mixed_content",
"lighthouse",
"cron",
"application_health",
"sitemap",
"dns",
"domain",
"certificate_health",
"ports",
"dns_blocklist"
],
"group_name": "Internal",
"friendly_name": "Documentation",
"tags": ["internal", "production", "docs"],
"notes": "Internal notes for our team members",
"real_ip_address": "176.62.160.106",
"description": "Oh Dear documentation",
"uptime_check_settings": {
"location": "paris",
"expected_final_redirect_url": "https://redirect-to-this-url.com",
"failed_notification_threshold": 2,
"http_verb": "get",
"timeout": 5,
"max_redirect_count": 5,
"payload": [
{
"name": "payload-name-1",
"value": "payload-value-1"
}
],
"valid_status_codes": ["2*"],
"look_for_string": null,
"absent_string": null,
"expected_response_headers": [
{
"name": "my-response-header",
"condition": "equals",
"value": "my-response-value"
}
],
"http_client_headers": [
{
"name": "my-header",
"value": "my-value"
}
]
},
"performance_check_settings": {
"threshold_in_ms": 3500,
"change_percentage": 50
},
"crawler_settings": {
"headers": [
{
"name": "my-broken-links-header",
"value": "my-broken-links-value"
}
]
},
"broken_links_check_settings": {
"whitelisted_urls": [],
"check_include_external_links": false,
"types": ["link", "image", "script", "stylesheet", "og:image"],
"force_crawl_urls": [],
"new_links_only": false
},
"sitemap_check_settings": {
"path": "/sitemap.xml",
"speed": "slow"
},
"application_health_check_settings": {
"result_url": "https://mybrandnewsite.tld/health",
"headers": [
{"name": "my-header", "value": "my-value"}
]
},
"certificate_health_check_settings": {
"expires_soon_threshold_in_days": 14
},
"dns_check_settings": {
"extra_cnames": ["cname1", "cname2"],
"monitor_main_domain": false,
"ignored_record_types": ["A", "CNAME"],
"check_nameservers_in_sync": true
},
"domain_check_settings": {
"expires_soon_threshold_in_days": 30
},
"lighthouse_check_settings": {
"continent": "europe",
"device_emulation": "desktop"
},
"ports_check_settings": {
"expected_open_ports": [80, 443],
"continent": "europe"
},
"send_report_to_emails": [
"first@example.com",
"second@example.com"
],
"include_check_types_in_report": [
"uptime",
"performance",
"broken_links",
"mixed_content",
"lighthouse",
"cron",
"application_health",
"sitemap",
"dns",
"domain",
"certificate_health",
"ports",
"dns_blocklist"
]
}
Error handling #
If an error occurred, you'll be given a non-HTTP/200 response code. The resulting payload might look like this:
{
"message": "The given data was invalid.",
"errors": {
"url": ["The url field is required."],
"team_id": ["The team id field is required."]
}
}
If you've got both the url and the team_id in your payload, but the data is invalid, you'll get a detailed error message:
{
"message": "The given data was invalid.",
"errors": {
"url": ["You should enter a url that starts with either \"http://\" or \"https://\"."],
"team_id": ["You do not belong to that team."]
}
}
Updating monitor settings #
PUT /api/monitors/{monitorId}
All fields are optional -- only include what you want to change.
The payload accepts the same fields as creating a monitor with custom settings with the following differences:
url is not required (but can be updated if needed)
team_id is not allowed as a monitor cannot be moved between teams
type is not allowed as a monitor's type cannot be changed after creation
Returns the updated monitor object.
Deleting a monitor #
DELETE /api/monitors/{monitorId}
Deletes the specified monitor and all its checks.
Returns 204 No Content on success.
Ignoring broken links URLs #
POST /api/monitors/{monitorId}/add-to-broken-links-whitelist
Add a single URL to the broken links whitelist. This is primarily used to quickly whitelist a single URL from an overview screen.
Request body (JSON):
whitelistUrl (string, required) -- the URL to add to the whitelist
{
"whitelistUrl": "https://externalsite.tld/page1"
}
For bulk whitelist management, use the update endpoint with the broken_links_check_settings.whitelisted_urls field.
Modifying individual monitor settings #
Use the update endpoint with only the fields you want to change. You don't need to send the full payload.
For example, to enable external link checking:
{
"broken_links_check_settings": {
"check_include_external_links": true,
"whitelisted_urls": ["https://externalsite.tld/page1", "https://externalsite.tld/page2"]
}
}
Or to update the uptime check payload:
{
"uptime_check_settings": {
"payload": [{"name": "my-name", "value": "my-value"}]
}
}