Monitors
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, "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 }, "sitemap_check_settings": { "path": "sitemap.xml", "speed": "default" }, "ports_check_settings": { "expected_open_ports": [80, 443], "expected_closed_ports": [22, 3306], "timeout_per_port_ms": 3000, "failure_threshold": 2 }, "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 monitorteam_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 monitorsurl: 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 andwww.prefix, used for sortinglabel: 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 monitordescription: Optional description for the monitornotes: Custom notes for the monitorlatest_run_date: Timestamp (UTC) of when any check for this monitor last ransummarized_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 checktype: 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 checkenabled: Whether this check is currently activelatest_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 statussettings: 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 settingslocation: Server location for checks (e.g.,"paris")look_for_string: Text that must be present in responseabsent_string: Text that must NOT be present in responseexpected_response_headers: Headers that must be present (with conditions)failed_notification_threshold: How many failures before alertinghttp_verb: HTTP method to use ("get","post", etc.)payload: Request body for POST/PUT requeststimeout: Request timeout in secondsvalid_status_codes: Array of acceptable HTTP status codes (e.g.,["2*", "3*"])max_redirect_count: Maximum redirects to followexpected_final_redirect_url: Expected final URL after redirectshttp_client_headers: Custom headers to send with requests
Ping Monitors (type: "ping"):
uptime_check_settings: ICMP ping settingslocation: Server location for checksttl: Time-to-live for ping packetscount: Number of ping packets to sendpacket_size_in_bytes: Size of each ping packetacceptable_packet_loss_percentage: Maximum acceptable packet lossacceptable_average_response_time_in_ms: Maximum acceptable average response timetimeout_in_seconds: Timeout for each pinginterval_in_seconds: Interval between pingsfailed_notification_threshold: How many failures before alerting
TCP Monitors (type: "tcp"):
uptime_check_settings: TCP connection settingslocation: Server location for checkstcp_send_string: String to send after connectingtimeout_in_ms: Connection timeout in millisecondslook_for_string_in_welcome_message: Text to look for in initial server responselook_for_string_in_send_string_response: Text to look for in response to sent stringfailed_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 monitoringexpires_soon_threshold_in_days: Days before expiration to warn
broken_links_check_settings: Broken link detectionwhitelisted_urls: URLs to ignore if brokencheck_include_external_links: Whether to check external linkstypes: 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 crawlnew_links_only: Only check newly discovered links
dns_check_settings: DNS monitoring configurationextra_cnames: Additional CNAME records to monitormonitor_main_domain: Whether to monitor the main domainignored_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 configurationcontinent: Geographic region for testing ("europe","north-america","asia")device_emulation: Device to emulate ("desktop"or"mobile")notification_settings: Custom notification thresholdshttp_client_headers: Custom headers for the audit
application_health_check_settings: Custom application health endpointresult_url: URL of your health check endpointsecret: Secret token for authenticationheaders: Custom headers to send
domain_check_settings: Domain expiration monitoringnot_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 monitoringthreshold_in_ms: Maximum acceptable response timechange_percentage: Percentage change threshold for alerts
sitemap_check_settings: Sitemap validationpath: Path to sitemap file (e.g.,"sitemap.xml")speed: Crawl speed ("default","fast","slow")
ports_check_settings: Port availability monitoringexpected_open_ports: Array of ports that should be open (max 20, values 1-65535)expected_closed_ports: Array of ports that should be closed (max 20, values 1-65535)timeout_per_port_ms: Timeout per port in milliseconds (500-10000)failure_threshold: How many consecutive failures before alerting (default: 2)
dns_blocklist_check_settings: DNS blocklist monitoringuse_all_blocklists: Whether to check all available blocklists (default: true)enabled_blocklists: Array of specific blocklist names to check (only used whenuse_all_blocklistsis false)
ai_check_settings: AI verification monitoring (only fortype: "ai"monitors)prompt: The AI prompt used to verify the page contentfrequency_unit/frequency_value: How often the AI check runssuppress_repeated_success_notifications: Whether repeated success notifications are suppressedthrottle_failed_interval_unit/throttle_failed_interval_value: Failure notification throttling intervalhtml_stripping_level: How much HTML to strip before sending to AI ("basic","aggressive", or"none")locationandwizard_*fields may also be present in responses
Additional properties #
crawler_headers: Custom headers sent during crawling operationssend_report_to_emails: Email addresses that receive the monthly reportsinclude_check_types_in_report: Which check types are included in monthly reportsbadge_id: Unique identifier for status badge integrationmarked_for_deletion_at: Timestamp (UTC) if monitor is scheduled for deletioncreated_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 monitorteam_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 verificationchecks(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.secretshould 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", "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], "expected_closed_ports": [22, 3306], "timeout_per_port_ms": 3000 }, "send_report_to_emails": [ "[email protected]", "[email protected]" ], "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:
urlis not required (but can be updated if needed)team_idis not allowed as a monitor cannot be moved between teamstypeis 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"}] } }
Feel free to reach out via [email protected] or on X via @OhDearApp if you have any other questions. We'd love to help!