The Oh Dear API lets you configure everything about our application through a simple, structured Application Programming Interface (API).
Everything you see in your dashboard can be controlled with the API. And as a bonus, all changes you make with the API will be visible in realtime on your dashboard.

Notice
On 23rd August 2025, we've made breaking changes to our API. You'll find more info on why we made these changes and how to upgrade on this page.
Getting started
Let's help get you started.
- Learn about our API authentication and generate your first API key
- Retrieve a list of all your monitors and their checks with the API.
- Configure your status pages automatically.
Optionally, we provide a PHP SDK package to help get you started and a list of data types we use in our API.
API endpoints
All Oh Dear API endpoints are located at https://ohdear.app/api
. From there on, you will find a logical structure that follows to the REST standard.
Here's a quick summary of the API methods.
GET
: all GET requests are for data retrieval only (site listing, account info, ...) and will never modify any data.
POST
: a POST method will add new monitors or checks to Oh Dear
DELETE
: the DELETE method is used to delete certain monitors or checks from your account.
PUT
: this method is used to update information on existing monitors, checks or your account.
In general, GET
requests can be performed as many times as you'd like (they are idempotent), all other methods actually transform data in your account and will cause changes to take effect.
Response data
All responses from the Oh Dear API will be formatted as JSON.
Here's an example payload of the /api/monitors
endpoint, that lists all monitors.
{
"data": [
{
"id": 1,
"url": "https://yoursite.tld",
...
},
{
"id": 2,
"url": "https://yourothersite.tld",
...
}
]
}
Each endpoint will return specific data for that request. More info on that in the monitors and checks pages.
Get your API token
Once you are logged in to your dashboard navigate to API access and create your API token.
You can name your token, so you know where this one is going to be used, in case you ever need to revoke it.

Once you create a token, it'll be shown only once to you. Make sure to store it safely, after this it can only be revoked and you can generate a new token for use.
Once you have your token, you can authenticate against the application.
By default, tokens can perform any actions to everything in your Oh Dear account. You can also create API tokens that only can be used in API calls to a selection of monitors and status pages.

Authenticate against the API
The token you've received can be used as the Authorization
header.
Here's a curl example of how you can authenticate against the API. In this example, it'll list all monitors in your account.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The token bgUKSWYL30iHg5w0WTDGHfubt5L1HBTr0atAehCeSqwNTqkU9rOmsNEmWf6Y
is used to authenticate. The /api/monitors
endpoint is hit to retrieve all monitors. The resulting response payload may look like this.
{
"data": [
{
"id": 1,
"url": "https://yoursite.tld",
...
},
{
"id": 2,
"url": "https://yourothersite.tld",
...
}
]
}
The actual payload is usually much bigger, but that's described in more detail in the monitors and checks page.
Data types
Check Types
uptime
performance
broken_links
mixed_content
lighthouse
cron
application_health
sitemap
dns
domain
certificate_health
Link Types
link
image
script
stylesheet
og:image
Check Results
pending
succeeded
warning
failed
errored-or-timed-out
Summarized Check Results
Crawler/Sitemap Speed
slowest
slow
default
fast
fastest
Lighthouse Slowdown Modifier
0
(fastest) to
5
(slowest)
Lighthouse continent
europe
north-america
asia
contains
not contains
equals
matches pattern
Uptime Check Locations
Africa
Asia
bangalore
seoul
singapore
tokyo
Australia
Canada
Europe
frankfurt
london
paris
stockholm
Middle East
South America
US East
US Mid
US West
los angeles
san francisco
DNS Record Types
A
AAAA
CAA
CNAME
MX
NS
SOA
SRV
TXT
PTR
Uptime check HTTP Verb
Monitors
Get all monitors in your account
The /api/monitors
endpoint lists all your monitors in your account.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
As a result, you get the details for every site you've added.
{
"data": [
{
"id": 1,
"team_id": 1,
"type": "http",
"url": "https://freek.dev",
"uses_https": true,
"sort_url": "freek.dev",
"label": "freek.dev",
"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": {
"uptime_check_location": "paris",
"uptime_check_expected_final_redirect_url": null,
"uptime_check_failed_notification_threshold": null,
"uptime_check_http_verb": null,
"uptime_check_timeout": null,
"uptime_check_max_redirect_count": null,
"uptime_check_payload": null,
"uptime_check_valid_status_codes": null,
"uptime_check_look_for_string": null,
"uptime_check_absent_string": null,
"uptime_check_expected_response_headers": null,
"http_client_headers": null
},
"active_snooze": null
},
{
"id": 101,
"type": "broken_links",
"label": "Broken links",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:05",
"latest_run_result": "succeeded",
"summary": "None",
"settings": {
"broken_links_check_include_external_links": null,
"broken_links_whitelisted_urls": null,
"crawler_headers": [],
"crawler_speed": "default",
"broken_link_types": null,
"respect_robots": true
},
"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",
"cpu_slowdown_modifier": 0,
"notification_settings": null,
"preferred_server_name": "lighthouse-checker-frankfurt-1",
"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"
},
"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"
},
{
"id": 2,
"team_id": 1,
"type": "ping",
"url": "spatie.be",
"uses_https": false,
"sort_url": "spatie.be",
"label": "spatie.be",
"group_name": "",
"tags": [],
"description": null,
"notes": null,
"latest_run_date": "2019-09-16 07:30:00",
"summarized_check_result": "succeeded",
"checks": [
{
"id": 200,
"type": "uptime",
"label": "Uptime",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:30:00",
"latest_run_result": "succeeded",
"summary": "Up",
"settings": {
"uptime_check_location": "paris",
"uptime_check_expected_final_redirect_url": null,
"uptime_check_failed_notification_threshold": null,
"uptime_check_http_verb": null,
"uptime_check_timeout": null,
"uptime_check_max_redirect_count": null,
"uptime_check_payload": null,
"uptime_check_valid_status_codes": null,
"uptime_check_look_for_string": null,
"uptime_check_absent_string": null,
"uptime_check_expected_response_headers": null,
"http_client_headers": null
},
"active_snooze": null
}
],
"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,
"timeout_in_seconds": 1,
"interval_in_seconds": 1,
"failed_notification_threshold": 2
},
"badge_id": "badge-456",
"created_at": "2019-09-16 07:26:00",
"updated_at": "2019-09-16 07:26:00"
},
{
"id": 3,
"team_id": 1,
"type": "tcp",
"url": "smtp.gmail.com:3306",
"uses_https": false,
"sort_url": "smtp.gmail.com:3306",
"label": "smtp.gmail.com:3306",
"group_name": "",
"tags": [],
"description": null,
"notes": null,
"latest_run_date": "2019-09-16 07:31:00",
"summarized_check_result": "succeeded",
"checks": [
{
"id": 300,
"type": "uptime",
"label": "Uptime",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:31:00",
"latest_run_result": "succeeded",
"summary": "Up",
"settings": {
"uptime_check_location": "paris",
"uptime_check_expected_final_redirect_url": null,
"uptime_check_failed_notification_threshold": null,
"uptime_check_http_verb": null,
"uptime_check_timeout": null,
"uptime_check_max_redirect_count": null,
"uptime_check_payload": null,
"uptime_check_valid_status_codes": null,
"uptime_check_look_for_string": null,
"uptime_check_absent_string": null,
"uptime_check_expected_response_headers": null,
"http_client_headers": null
},
"active_snooze": null
}
],
"uptime_check_settings": {
"location": "paris",
"tcp_send_string": "",
"timeout_in_ms": 1000,
"look_for_string_in_welcome_message": "",
"look_for_string_in_send_string_response": "",
"failed_notification_threshold": 2
},
"badge_id": "badge-789",
"created_at": "2019-09-16 07:27:00",
"updated_at": "2019-09-16 07:27:00"
}
],
"links": {
"first": "https://ohdear.app/api/monitors?page%5Bnumber%5D=1",
"last": "https://ohdear.app/api/monitors?page%5Bnumber%5D=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://ohdear.app/api/monitors",
"per_page": 200,
"to": 3,
"total": 3
}
}
Let's drill this down. The three monitors shown represent different monitor types:
-
HTTP Monitor (type: "http"
): This is an https://
website monitor that gets comprehensive monitoring including uptime, performance, certificate health, broken links, mixed content, lighthouse, DNS, domain expiration, etc.
-
Ping Monitor (type: "ping"
): This monitors server availability using ICMP ping. It only has uptime checks and uses different settings (TTL, packet size, packet loss percentage, etc.).
-
TCP Port Monitor (type: "tcp"
): This monitors specific TCP port availability (like MySQL, SMTP, etc.). It has uptime checks with TCP-specific settings like connection strings and timeouts.
Each monitor type has different available checks and settings based on what can be monitored. More on each check in our dedicated checks page.
Also notice that the content of the uptime_check_settings
depends on the type of monitor.
Get a specific monitor via the API
If you don't want to retrieve all monitors, you can get a specific monitor if you know its ID. The example below will get the details of monitor ID 99.
$ 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'
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": {
"uptime_check_location": "paris"
},
"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": []
},
"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"
}
Notice how this payload does not contain the data
wrapper (the API call to retrieve all monitors does).
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, or "tcp"
for TCP port monitoring
url
: The URL/hostname you submitted to Oh Dear (includes protocol for HTTP monitors, hostname for ping, hostname:port for TCP)
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
latest_run_date
: Timestamp of when any check for this monitor last ran
summarized_check_result
: Overall monitor health - "succeeded"
, "warning"
, "failed"
, or "pending"
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
)
label
: Human-readable label for the check
enabled
: Whether this check is currently active
latest_run_ended_at
: When this specific check last completed
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
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
: Types of links to check (["link", "image", "script", "stylesheet", "og:image"])
do_not_crawl_urls
: URLs to exclude from crawling
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 (["SOA"])
check_nameservers_in_sync
: Whether to verify nameservers are synchronized
lighthouse_check_settings
: Performance audit configuration
continent
: Geographic region for testing ("europe", "america", etc.)
cpu_slowdown_modifier
: CPU throttling factor
notification_settings
: Custom notification thresholds
preferred_server_name
: Specific server to use for testing
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
sitemap_check_settings
: Sitemap validation
path
: Path to sitemap file ("sitemap.xml")
speed
: Crawl speed ("default", "fast", "slow")
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 if monitor is scheduled for deletion
created_at
: When the monitor was created
updated_at
: When the monitor was last modified
Getting a summary of the results of a check
Oh Dear can perform various checks (uptime, mixed content, broken links, ...) for your monitors.
If you want to know the status of a particular check, you can query the /api/monitors/{monitorId}/check-summary/{checkType}
endpoint.
The checkType
segment can be one of the check types we support.
Here's an example:
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/99/check-summary/uptime \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The response holds two properties:
result
: this is the result of the check. Possible values are pending
, succeeded
, warning
, failed
or errored-or-timed-out
.
summary
: a human-readable summary of the check result
Add a monitor through the API
To add a monitor, create a POST
call to the /api/monitors/
endpoint. Here's an example to add the monitor https://mybrandnewsite.tld
to Oh Dear.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"url":"https://example.com", "team_id":"1", "type":"http"}'
Remember that we love JSON? Your request payload should also be valid JSON. The minimum actual payload, when formatted, looks like this.
{
"url": "https://example.com",
"team_id": "1",
"type": "http"
}
Mandatory fields are:
url
: The URL/hostname to monitor
team_id
: The team where this monitor should be added to
type
: The monitor type - "http"
for websites, "ping"
for ICMP ping monitoring, or "tcp"
for TCP port monitoring
To find out the teams you belong to, see the user info API call.
If the API call succeeded, you'll be given output like this, showcasing every check that was added:
{
"id": 173,
"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": null,
"summarized_check_result": "pending",
"checks": [
{
"id": 560,
"type": "uptime",
"label": "Uptime",
"enabled": true,
"latest_run_ended_at": null,
"latest_run_result": "pending",
"summary": "Pending",
"settings": {
"uptime_check_location": "paris"
},
"active_snooze": null
},
{
"id": 561,
"type": "certificate_health",
"label": "Certificate health",
"enabled": true,
"latest_run_ended_at": null,
"latest_run_result": "pending",
"summary": "Pending",
"settings": {
"certificate_health_check_settings": {
"expires_soon_threshold_in_days": null
}
},
"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": []
},
"badge_id": "badge-abc123",
"marked_for_deletion_at": null,
"created_at": "2019-09-16 08:00:00",
"updated_at": "2019-09-16 08:00:00"
}
Add a monitor with enabled checks
When creating a site you can control which checks get enabled. You can do so by passing an array with one of these check types.
Here's an example payload where a site is created with only the uptime
and mixed_content
checks enabled.
{
"url": "https://mybrandnewsite.tld",
"team_id": "1",
"checks": ["uptime", "mixed_content"]
}
For more details on each check, have a look at the checks API endpoint.
Add a monitor with custom settings
If you need to control the settings of a site, you can pass additional parameters in the payload. Here's an example of a site with all possible settings. You can also control the monthly site report settings in this payload using the send_report_to_emails
and include_check_types_in_report
settings.
Please be aware:
url
is required
team_id
is required and should be a team you belong to
checks
is required and should be an array of check types
application_health_check_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
{
"url": "https://ohdear.app/docs",
"team_id": 1,
"checks": [
"uptime",
"performance",
"broken_links",
"mixed_content",
"lighthouse",
"cron",
"application_health",
"sitemap",
"dns",
"domain",
"certificate_health"
],
"group_name": "Internal",
"friendly_name": "Documentation",
"tags": ["internal", "production", "docs"],
"notes": "Internal notes for our team members",
"description": "Oh Dear documentation",
"uptime_check_location": "paris",
"uptime_check_expected_final_redirect_url": "https://redirect-to-this-url.com",
"uptime_check_failed_notification_threshold": 2,
"uptime_check_http_verb": "get",
"uptime_check_timeout": 5,
"uptime_check_max_redirect_count": 5,
"uptime_check_payload": [
{
"name": "payload-name-1",
"value": "payload-value-1"
}
],
"uptime_check_valid_status_codes": ["2*"],
"uptime_check_look_for_string": null,
"uptime_check_absent_string": null,
"uptime_check_expected_response_headers": [
{
"name": "my-response-header",
"condition": "equals",
"value": "my-response-value"
}
],
"http_client_headers": [
{
"name": "my-header",
"value": "my-value"
}
],
"performance_threshold_in_ms": 3500,
"performance_change_percentage": 50,
"crawler_headers": [
{
"name": "my-broken-links-header",
"value": "my-broken-links-value"
}
],
"broken_links_check_include_external_links": false,
"broken_link_types": ["link", "image", "script", "stylesheet", "og:image"],
"broken_links_whitelisted_urls": [],
"respect_robots": true,
"sitemap_path": "/sitemap.xml",
"sitemap_speed": "slow",
"application_health_check_result_url": "https://mybrandnewsite.tld/health",
"application_health_headers": [
{"name": "my-header", "value": "my-value"}
],
"certificate_health_check_expires_soon_threshold_in_days": 14,
"dns_check_nameservers_in_sync": true,
"dns_monitor_main_domain": false,
"dns_extra_cnames": ["cname1", "cname2"],
"dns_ignored_record_types": ["A", "CNAME"],
"domain_check_expires_soon_threshold_in_days": 30,
"lighthouse_check_continent": "europe",
"lighthouse_cpu_slowdown_modifier": 0,
"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",
]
}
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
You can update the monitor settings by performing a PUT
request to the /api/monitors/{monitorId}
endpoint.
The payload is the same as creating a monitor wit the following differences:
- not all keys are required (only the ones you want to update)
url
is not required (but can be updated if needed)
team_id
is not allowed as a monitor cannot be moved between teams
{
"url": "https://example.com/docs",
"type": "http",
"checks": [
"uptime",
"performance",
"broken_links",
"mixed_content",
"lighthouse",
"cron",
"application_health",
"sitemap",
"dns",
"domain",
"certificate_health"
],
"group_name": "Internal",
"label": "Documentation",
"tags": ["internal", "production", "docs"],
"notes": "Internal notes for our team members",
"description": "Documentation site monitoring",
"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
},
"broken_links_check_settings": {
"check_include_external_links": false,
"types": ["link", "image", "script", "stylesheet", "og:image"],
"whitelisted_urls": [],
"respect_robots": true
},
"sitemap_check_settings": {
"path": "/sitemap.xml",
"speed": "slow"
},
"application_health_check_settings": {
"result_url": "https://example.com/health",
"secret": null,
"headers": [
{
"name": "my-header",
"value": "my-value"
}
]
},
"certificate_health_check_settings": {
"expires_soon_threshold_in_days": 14
},
"dns_check_settings": {
"check_nameservers_in_sync": true,
"monitor_main_domain": false,
"extra_cnames": ["cname1", "cname2"],
"ignored_record_types": ["A", "CNAME"]
},
"domain_check_settings": {
"expires_soon_threshold_in_days": 30
},
"lighthouse_check_settings": {
"continent": "europe",
"cpu_slowdown_modifier": 0
},
"crawler_headers": [
{
"name": "my-broken-links-header",
"value": "my-broken-links-value"
}
],
"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"
]
}
Deleting a monitor
Use the DELETE
method to delete a monitor through the API. In this example, we'll delete the monitor with ID 1
.
$ OHDEAR_TOKEN="your API token"
$ curl -X DELETE https://ohdear.app/api/monitors/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Notice how we're calling the DELETE
method on the /api/monitors/1
endpoint. The 1
in the URL determines which monitor to delete.
Ignoring broken links URLs
You can add a single URL to our crawler ignore list with an easy API method. This is primarily used to quickly add just a single URL, for instance in an overview-screen where you allow users to whitelist URLs.
If you want to ignore several URLs, or remove ignored URLs, please see the more extended update-broken-links-settings API call.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/add-to-broken-links-whitelist \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"whitelistUrl": "https://externalsite.tld/page1"
}'
Modifying monitor settings
We now support updating all monitor settings from a single endpoint. But we still support updating some settings on the following dedicated endpoints.
Broken links settings
You can control whether our crawler should check for external links or stick to internal pages only.
Additionally, you can update the monitor settings and ignore URLs that are known to be broken (or out of your control), so we no longer report if we find broken pages listed on them.
Note: this lists the source URLs, not the destinations.
$ OHDEAR_TOKEN="your API token"
$ curl -X PUT https://ohdear.app/api/monitors/1/update-broken-links-settings \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"broken_links_check_include_external_links": true,
"broken_links_whitelisted_urls_string":"
https://externalsite.tld/page1
https://externalsite.tld/page2"
}'
The inputs are as follows:
broken_links_check_include_external_links
: boolean value, either true
or false
broken_links_whitelisted_urls_string
: string value, new-line separated list of fully qualified URLs that we should ignore and not report on
Both can be used independently, if you just want to update the broken_links_check_include_external_links
setting, there is no need to also submit the list of ignored URLs.
Updating the uptime check payload
Oh Dear can send form data along with the uptime check for all non-GET requests.
You can update the sent payload by performing this PUT
request:
$ OHDEAR_TOKEN="your API token"
$ curl -X PUT https://ohdear.app/api/monitors/1/update-uptime-check-payload \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '[
{"name": "my-name", "value": "my-value"}
]'
Checks
Get all checks for a particular monitor
To know what checks are available to you, you'll need to query the monitors API call first. Per monitor, you'll be given a set of check IDs.
In this example, we'll get the details for monitor ID 99
. Notice the checks
data attribute.
$ 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'
Here's the response:
{
"id": 1,
"url": "https://yoursite.tld",
...
"checks": [
{
"id": 1,
"type": "uptime",
"label": "Uptime",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:02",
"latest_run_result": "succeeded",
"settings": []
},
{
"id": 2,
"type": "broken_links",
"label": "Broken links",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:05",
"latest_run_result": "failed",
"settings": []
},
{
"id": 3,
"type": "mixed_content",
"label": "Mixed content",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:05",
"latest_run_result": "succeeded",
"settings": []
},
{
"id": 4,
"type": "certificate_health",
"label": "Certificate health",
"enabled": true,
"latest_run_ended_at": "2019-09-16 07:29:02",
"latest_run_result": "succeeded",
"settings": []
}
]
}
Per check, you'll see its id
. Once you know that ID, you can enable or disable that check or request a new run, on the spot.
Enable & disable a check
Let's take the first check ID, 1
, as an example. First, we'll enable the check.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/checks/1/enable \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
You make a POST
call to /api/checks/{id}/enable
to enable that check. As a result, you'll be given a confirmation payload.
{
"id": 405,
"type": "uptime",
"enabled": true
}
To disable, make a similar POST
to the /disable
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/checks/1/disable \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The resulting payload will confirm the enabled
attribute is set to false.
{
"id": 405,
"type": "uptime",
"enabled": false
}
From that point forward, we'll no longer run that particular check.
If you want to trigger an on-demand run of a check, for instance a new mixed content or broken links scan, you can send an API call to the /request-run
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/checks/1/request-run \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
To confirm, we'll give the details of the check once again.
Snooze and unsnooze a check
You can temporarily snooze, or silence, a particular check. We will still perform the checks, but won't send out any notifications in the period that a check is snoozed. For more details on how this option works, have a look at our snooze documentation.
If you prefer to stop monitoring a check for a particular period, it's better to disable it and enable it again later on.
To snooze a check, you can call the /api/checks/{id}/snooze
endpoint and tell it how long the check should be snoozed using the minutes
payload, formatted in JSON.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/checks/1/snooze \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"minutes":"10"}'
If you receive an HTTP/1.1 200 OK
response, the snooze was applied correctly.
To unsnooze, or remove the silencing of alerts, you can call the /api/checks/{id}/unsnooze
endpoint.
$ curl -X POST https://ohdear.app/api/checks/1/unsnooze \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
If you once again received an HTTP/1.1 200 OK
response, the snooze was correctly removed.
Snooze and unsnooze a scheduled task
In addition to snoozing the entire check, you can also snooze individual scheduled tasks.
To snooze a specific scheduled task, you can call the /api/cron-checks/{task_id}/snooze
endpoint and tell it how long the check should be snoozed using the minutes
payload, formatted in JSON.
Call /api/cron-checks/{task_id}/unsnooze
to remove any active snooze for this task.
Tip: You can find the cron_id by calling the /api/monitors/{monitor_id}/cron-checks
endpoint or by looking at the cron check details in the Oh Dear application.
Snooze and unsnooze an application health check
You can also snooze and unsnooze an individual application health check.
To snooze an application health check, you can call the /api/monitors/{monitor_id}/application-health-checks/{health_check_id}/snooze
endpoint and tell it how long the check should be snoozed using the minutes
payload, formatted in JSON.
Call /api/monitors/{monitor_id}/application-health-checks/{health_check_id}/unsnooze
to remove any active snooze for this check.
Tip: You can find the application health check id by calling the /api/monitors/{monitor_id}/application-health-checks
endpoint or by looking at the application health check details in the Oh Dear application.
Return properties
The response gives you some basic details of a check.
id
: an internal identifier to Oh Dear
type
: what type of check this is. Possible values are: uptime
, performance
, broken_links
, certificate_health
, mixed_content
, cron
, dns
and application_health
.
label
: a human readable label of the check
enabled
: a boolean value to indicate if this check is enabled or not. Possible values are: true
or false
.
latest_run_ended_at
: a timestamp, in UTC, of the last time this check was run
latest_run_result
: the status of the last run, can be pending
, succeeded
, warning
or failed
summary
: a human readable summary of the check result
active_snooze
: an object containing the details of the active snooze for this check, or null
if no snooze is active
More properties will be added over time.
Active snooze
The active_snooze
property is an object containing the details of the active snooze for this check, or null
if no snooze is active.
"active_snooze": {
"id": 1,
"starts_at": "2025-04-30 11:35:20",
"ends_at": "2025-04-30 11:40:20",
"snoozed_by": "Sean White",
"unsnoozed_by": null,
"source": "API",
"created_at": "2025-04-30 11:35:20"
}
Uptime
If a monitor has the uptime check enabled, you can query the uptime percentages via the /api/uptime
endpoint. To use this endpoint you'll need to now the monitor id
of your monitor. You can get all monitors ids by calling the get all monitors endpoint.
To retrieve you'll need to specify a period via the started_at
and ended_at
filters. The period may not be longer than 2 years. The split
value determines how fine grained the answer periods should be. Valid values are hour
, day
and month
.
In this example we are going to get the uptime for monitor id 1.
$ OHDEAR_TOKEN="your API token"
$ curl "https://ohdear.app/api/monitors/1/uptime?filter[started_at]=20180101000000&filter[ended_at]=20180101235959&split=day" \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Here's what the output could look like:
[
{"datetime":"2018-01-01 00:00:00","uptime_percentage":100},
{"datetime":"2018-01-01 01:00:00","uptime_percentage":99.76},
{"datetime":"2018-01-01 02:00:00","uptime_percentage":100},
{"datetime":"2018-01-01 03:00:00","uptime_percentage":98.34},
...
{"datetime":"2018-01-01 23:00:00","uptime_percentage":100}
]
Downtime
You can query the downtime via the /api/monitors/{monitorId}/downtime
endpoint. To use this endpoint you'll need to now the monitor id
of your monitor. You can get all monitors ids by calling the get all monitors endpoint.
To retrieve you'll need to specify a period via the started_at
and ended_at
filters.
In this example we are going to get the downtime periods for monitor number 1.
$ OHDEAR_TOKEN="your API token"
$ curl "https://ohdear.app/api/monitors/1/downtime?filter[started_at]=20180101150000&filter[ended_at]=20180101160000" \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Here's what the output could look like:
{
"data": [
{
"id": 1,
"started_at": "2018-01-01 15:00:00",
"ended_at": "2018-01-01 15:07:00",
"notes_html": "We contacted our provider and they fixed the issue.",
"notes_markdown": "We contacted our provider and they fixed the issue."
}
]
}
Deleting a downtime period
You can delete a downtime period by calling the /api/downtime/{downtimeId}
endpoint. Make sure to use the DELETE
http verb. To use this endpoint you'll need to now the downtime id
, which you can get to by calling the get downtime endpoint.
Uptime Metrics
Retrieving uptime metrics
You can retrieve uptime metrics using different endpoints depending on your monitor type. Each monitor type has its own dedicated endpoint that returns metrics specific to that monitoring method. You'll need to know your monitor's ID first.
A filter is necessary: you decide from when to when the data should be returned.
In this example we'll get a list of all monitors and use the monitor ID to retrieve the uptime metrics.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"id": 1,
"url": "https://www.yoursite.tld",
"type": "http",
"...",
}
]
}
HTTP Website Uptime Metrics
For HTTP website monitors (type: http
), use the /api/http-uptime-metrics
endpoint to get detailed performance and timing data.
$ curl https://ohdear.app/api/monitors/1/http-uptime-metrics?filter[start]=20240419132501&filter[end]=20240420132501&filter[group_by]=minute \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"dns_time_in_seconds": 0.001399,
"tcp_time_in_seconds": 0.01651,
"ssl_handshake_time_in_seconds": 0.053852,
"remote_server_processing_time_in_seconds": 0.021595,
"download_time_in_seconds": 0.000535,
"total_time_in_seconds": 0.09389099999999999,
"curl": {
"namelookup_time": 0.001399,
"connect_time": 0.017909,
"appconnect_time": 0.071761,
"starttransfer_time": 0.093356,
"pretransfer_time": 0.071864,
"redirect_time": 0,
"total_time": 0.093891,
"header_size": 1261,
"size_download": 12814,
"speed_download": 137784
},
"date": "2024-04-19 13:25:00"
},
{
...
}
]
}
Ping Uptime Metrics
For ping monitors (type: ping
), use the /api/ping-uptime-metrics
endpoint to get ICMP ping response data.
$ curl https://ohdear.app/api/monitors/1/ping-uptime-metrics?filter[start]=20240419132501&filter[end]=20240420132501&filter[group_by]=minute \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"minimum_time_in_ms": 12.5,
"maximum_time_in_ms": 45.2,
"average_time_in_ms": 28.7,
"packet_loss_percentage": 0.0,
"uptime_percentage": 100.0,
"downtime_percentage": 0.0,
"uptime_seconds": 60,
"downtime_seconds": 0,
"date": "2024-04-19 13:25:00"
},
{
...
}
]
}
TCP Port Uptime Metrics
For TCP port monitors (type: tcp
), use the /api/tcp-uptime-metrics
endpoint to get TCP connection data.
$ curl https://ohdear.app/api/monitors/1/tcp-uptime-metrics?filter[start]=20240419132501&filter[end]=20240420132501&filter[group_by]=minute \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"time_to_connect_in_ms": 15.3,
"uptime_percentage": 100.0,
"downtime_percentage": 0.0,
"uptime_seconds": 60,
"downtime_seconds": 0,
"date": "2024-04-19 13:25:00"
},
{
...
}
]
}
If there are no metrics in the requested period, you'll receive an empty data
array.
{
"data": []
}
Selecting the granularity for data
In the examples above, we grouped the metrics per minute. These records are stored for roughly 14 days. We also store metrics per hour (which are kept for a couple of weeks) and per day (which are kept eternally).
You can request the hourly and daily records by passing hour
or day
to group_by
.
HTTP uptime metrics data explained
The API returns detailed performance metrics for HTTP monitors:
{
"dns_time_in_seconds": 0.001399,
"tcp_time_in_seconds": 0.01651,
"ssl_handshake_time_in_seconds": 0.053852,
"remote_server_processing_time_in_seconds": 0.021595,
"download_time_in_seconds": 0.000535,
"total_time_in_seconds": 0.09389099999999999,
"curl": {
"namelookup_time": 0.001399,
"connect_time": 0.017909,
"appconnect_time": 0.071761,
"starttransfer_time": 0.093356,
"pretransfer_time": 0.071864,
"redirect_time": 0,
"total_time": 0.093891,
"header_size": 1261,
"size_download": 12814,
"speed_download": 137784
},
"date": "2024-04-19 13:25:00"
}
In the curl
key, you find the raw performance values that curl
produces when we called your monitor. The timing values are expressed in seconds. So 0.0429
can be read as 42.9ms
. By multiplying each value by 1,000, you get the value in milliseconds.
The data represents the following part of the request:
dns_time_in_seconds
: The time it takes to resolve the domain name to an IP address via DNS.
tcp_time_in_seconds
: The time it takes to connect to the remote host (TCP three-way handshake).
ssl_handshake_time_in_seconds
: The total time it took for the TLS handshake to complete (cipher negotiation & encryption).
remote_server_processing_time_in_seconds
: The time it took the server to process the request and start sending the first byte of the page.
download_time_in_seconds
: The time, in seconds, it took for the page to be downloaded.
total_time_in_seconds
: The complete request duration from start to finish.
Ping uptime metrics data explained
The API returns ping-specific metrics for ping monitors:
minimum_time_in_ms
: The fastest ping response time in the period (milliseconds).
maximum_time_in_ms
: The slowest ping response time in the period (milliseconds).
average_time_in_ms
: The average ping response time in the period (milliseconds).
packet_loss_percentage
: Percentage of ping packets that were lost.
uptime_percentage
: Percentage of time the host was reachable via ping.
downtime_percentage
: Percentage of time the host was unreachable.
uptime_seconds
: Total seconds the host was reachable in this period.
downtime_seconds
: Total seconds the host was unreachable in this period.
TCP uptime metrics data explained
The API returns TCP connection metrics for TCP monitors:
time_to_connect_in_ms
: The time it took to establish a TCP connection (milliseconds).
uptime_percentage
: Percentage of time the port was accessible.
downtime_percentage
: Percentage of time the port was inaccessible.
uptime_seconds
: Total seconds the port was accessible in this period.
downtime_seconds
: Total seconds the port was inaccessible in this period.
Broken Links
Retrieving broken links
If a monitor has the broken links check enabled, you can get all detected broken links via the /api/broken-links
endpoint. To use this endpoint you'll need to now the monitor id of your monitor. You can get all monitors ids by calling the get all monitors endpoint.
In this example we are going to get the broken-links for monitor number 1.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/broken-links/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Here's what the output could look like:
{
"data": [
{
"crawled_url": "https://example.com/broken",
"relative_crawled_url": "/broken",
"status_code": 404,
"found_on_url": "https://example.com",
"relative_found_on_url": "https://example.com",
"internal": true
}
]
}
Mixed content
Retrieving mixed content
If a monitor has the mixed content check enabled, you can get all detected mixed content via the /api/mixed-content
endpoint. To use this endpoint you'll need to now the monitor id of your monitor. You can get all monitors ids by calling the get all monitors endpoint.
In this example we are going to get the broken-links for monitor number 1.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/mixed-content/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Here's what the output could look like:
{
"data": [
{
"element_name": "img",
"mixed_content_url": "http://example.com/image.jpg",
"found_on_url": "https://example.com"
}
]
}
Certificate Health
Retrieving info on the certificate health
If a monitor has the certificate health check enabled, you can get feedback on the result via the /api/certificate-health
endpoint. To use this endpoint you'll need to now the monitor id of your monitor. You can get all monitors ids by calling the get all monitors endpoint.
Here's an example where we get the certificate health of the monitor with ID 1.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/certificate-health/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The output will be an object with these 3 keys:
certificate_details
: an array with some details on the detected certificate
certificate_checks
: an array with info on the checks we performed on the certificate
certificate_chain_issuers
: an array containing the names of all the issuers in the complete certificate chain
Here's what it looks like.
{
"certificate_details": {
"issuer": "Let's Encrypt Authority X3",
"valid_from": "2019-09-10 15:16:01",
"valid_until": "2019-12-09 15:16:01"
},
"certificate_checks": [
{
"type": "notFound",
"label": "Certificate present",
"passed": true
},
{
"type": "expiresSoon",
"label": "Will not expire in the next 14 days",
"passed": true
},
{
"type": "coversWrongDomain",
"label": "Covers the right domain",
"passed": true
},
{
"type": "doesNotConnectWithRootCertificate",
"label": "Connects with a root certificate",
"passed": true
},
{
"type": "notYetActive",
"label": "Is currently active",
"passed": true
},
{
"type": "isSelfSigned",
"label": "Is not self signed",
"passed": true
},
{
"type": "usesInvalidHash",
"label": "Uses valid hash",
"passed": true
},
{
"type": "hasExpired",
"label": "Has not expired",
"passed": true
},
{
"type": "hasChanged",
"label": "Unchanged since last checked",
"passed": true
}
],
"certificate_chain_issuers": [
"US, Let's Encrypt, Let's Encrypt Authority X3",
"Digital Signature Trust Co., DST Root CA X3"
]
}
To determine the overal health of the certificate, you can look at all the checks in the certificate_checks
result.
Those with a value of "passed": false
have a failed check.
Retrieving detected certificates
This endpoint allows you to view all certificates that have been detected for a specific monitor, including historical certificates.
Here's an example where we get all detected certificates for the monitor with ID 1.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/detected-certificates \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The response will contain a paginated list of detected certificates:
{
"data": [
{
"id": 123,
"monitor_id": 1,
"fingerprint": "abcd1234567890abcd1234567890abcd12345678",
"certificate_details": {
"issuer": "Let's Encrypt Authority X3",
"domain": "example.com",
"additional_domains": [
"www.example.com"
],
"valid_from": "2023-10-01T12:00:00Z",
"valid_until": "2024-01-01T12:00:00Z",
"days_until_expiration": 45,
"signature_algorithm": "sha256WithRSAEncryption",
"is_valid": true,
"is_expired": false
},
"created_at": "2023-10-01T12:30:00Z",
"updated_at": "2023-10-01T12:30:00Z"
}
],
"links": {
"first": "https://ohdear.app/api/monitors/1/detected-certificates?page=1",
"last": "https://ohdear.app/api/monitors/1/detected-certificates?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"per_page": 15,
"to": 1,
"total": 1
}
}
You can also retrieve a specific detected certificate by its ID:
$ curl https://ohdear.app/api/monitors/1/detected-certificates/123 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Filtering and sorting
You can filter detected certificates by fingerprint:
$ curl https://ohdear.app/api/monitors/1/detected-certificates?filter[fingerprint]=abcd1234567890abcd1234567890abcd12345678 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
You can sort the results by created_at
, updated_at
, or fingerprint
:
$ curl https://ohdear.app/api/monitors/1/detected-certificates?sort=-created_at \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Use a minus sign (-
) before the field name to sort in descending order.
User Info
Retrieve your team & user info
Make a GET
request to the /api/me
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/me \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
If the API call succeeded, you'll be presented with your team & user info.
Return properties
Here's an example payload of the API call.
{
"id": 1,
"name": "Firstname Lastname",
"email": "[email protected]",
"photo_url": "https://www.gravatar.com/avatar/...jpg",
"teams": [
{
"id": 1,
"name": "Your Team Name"
}
]
}
Let's look at all the properties that are returned.
id
: the identifier of the user this API key belongs to.
name
, email
and photo_url
: personal information about your account.
teams
: an array of teams this user belongs to. A user can be a member of several teams.
The teams
property has an id
(team ID) and a human readable name
. The team id
will be used throughout all API calls to determine what team you want to add monitors to.
Status Pages
You can make a GET
HTTP call to the /api/status-pages
endpoint to retrieve all your configured status pages.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/status-pages \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
As a result, you'll get a list of all your status pages and their metadata.
{
"data": [
{
"id": 1,
"team": {
"id": 1,
"name": "Your Team Name"
},
"title": "Your Status Page Title",
"domain": "status.your-domain.tld",
"slug": "autem-quos-accusantium-esse-ex-numquam-odio",
"full_url": "https://ohdear.app/status-page/autem-quos-accusantium-esse-ex-numquam-odio",
"timezone": "Europe/Brussels",
"summarized_status": "up",
"monitors": [
{
"id": 1,
"url": "https://site1.tld",
...
},
{
"id": 7,
"url": "https://site2.tld",
...
}
],
"created_at": "2019-09-13 07:06:51",
"updated_at": "2019-09-13 07:06:51"
}
],
"links": {
"first": "https://ohdear.app/api/status-pages?page%5Bnumber%5D=1",
"last": "https://ohdear.app/api/status-pages?page%5Bnumber%5D=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://ohdear.app/api/status-pages",
"per_page": 200,
"to": 2,
"total": 2
}
}
There's a lot of data in there, let's unpack it.
The following fields are available in the output:
data
: this is an array that contains a list of all your status pages.
team
: because you can belong to multiple teams, we'll clarify which team this status page belongs to.
summarized_status
: this can either be up
, down
or unknown
. It's down
when at least one of the monitors in the status page has an uptime check that reported it as down.
monitors
: this is an array with all monitors that are added to this status page. It will also contain all checks from each website, so you have all information easily available to you.
prevent_indexing
: this is a boolean value that determines whether the status page should be indexed by search engines
- the
links
and meta
output are for pagination when you have many status pages.
Retrieve a single status page
You can also pass the ID of a status page to the /api/status-pages/{id}
endpoint to get the details of a specific status page.
In this example, we'll retrieve the status page with ID 1.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/status-pages/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The resulting output will contain the details of that specific status page. Note that it does not have the data
container that is used when retrieving all monitors through the API.
{
"id": 1,
"team": {
"id": 1,
"name": "Your Team Name"
},
"title": "Your Status Page Title",
...
"updates": [
{
"id": 65,
"title": "Your status page update title",
...
},
...
],
"monitors": [
{
"id": 1,
"url": "https://site1.tld",
},
...
]
}
In addition to the similar output of the /api/status-pages
endpoint described above, when you get the data from a single status page we'll also include its updates
.
This will contain an array of all status page updates & messages that have been posted to your status page.
For more details, have a look at our status page updates API documentation.
The rest of the data is similar to the output described above.
Create a status page
You can create a status page by making a POST
HTTP call to the /api/status-pages
endpoint. The following fields are required:
team_id
: the ID of the team you want to create the status page for
title
: the title of your status page
monitors
: an array of monitors you want to add to the status page
id
: the ID of the monitor you want to add to the status page
clickable
: boolean whether the monitor should be clickable on the status page
{
"team_id": 1,
"title": "My New Status Page",
"monitors": [
{ "id": 1, "clickable": true },
{ "id": 2, "clickable": false }
]
}
Delete a status page
If you make a DELETE
HTTP call to the /api/status-pages/ID
, you can delete a status page through the API.
In this example we are going to delete the status page with ID 99.
$ OHDEAR_TOKEN="your API token"
$ curl -i -X DELETE https://ohdear.app/api/status-pages/99 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
Add monitors to a status page
You can add a monitor to an existing status page by making a POST
HTTP call to the /api/status-pages/{statusPage}/monitors
endpoint. The following fields are accepted:
sync
: (optional) boolean whether existing monitors should be removed and replaced with the given monitors
monitors
: an array of monitors you want to add to the status page
id
: the ID of the monitor you want to add to the status page
clickable
: boolean whether the monitor should be clickable on the status page
{
"sync": true,
"monitors": [
{ "id": 17, "clickable": true }
]
}
The sync
field is optional. If it's set to true
, the existing monitors will be removed and replaced with the given monitors. If it's set to false
, the given monitors will be appended to the status page.
Remove a monitor from a status page
You can also remove a monitor from a status page by making a DELETE
HTTP call to the /api/status-pages/{statusPage}/monitors/{monitor}
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl -i -X DELETE https://ohdear.app/api/status-pages/1/monitors/17 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
A HTTP/1.1 204 No Content
response indicates that the monitor was successfully removed from the status page.
HTTP/1.1 204 No Content
X-RateLimit-Limit: 180
X-RateLimit-Remaining: 176
...
Each status page can contain updates or messages. These are used to communicate to your customers or clients in times of downtime or maintenance.
You can post these messages from your dashboard or with our API.
To list the updates or messages from a single status page, you'll need to know its ID. You can retrieve that using the /api/status-pages
API call explained in the status pages API documentation.
Once you have your ID, you can get the details as follows:
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/status-pages/1/updates \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The resulting output will contain an array of all the updates to that specific status page.
If there are no status messages, the array will be empty.
{
"data": []
}
If there are messages, we'll return those in the data
field.
{
"data": [
{
"id": 65,
"title": "3rd party API issue resolved",
"text": "We have contacted our API provider and they have resolved the situation, everything is back to normal. We apologize for the inconvenience!",
"pinned": false,
"severity": "resolved",
"time": "2019-09-16 08:24:00",
"status_page_url": "https://ohdear.app/status-page/your-slug"
},
{
"id": 64,
"title": "We are experiencing an outage on our service",
"text": "Due to errors in our 3rd party API provider, we are experiencing an outage of our website. Our team is working on getting it resolved ASAP.",
"pinned": false,
"severity": "high",
"time": "2019-09-16 08:11:00",
"status_page_url": "https://ohdear.app/status-page/your-slug"
}
]
}
These are sorted by newest message to oldest. The output contains the following fields:
id
: the ID of that particular update
title
and text
: these are shown on your status page
pinned
: when set to true
, this update will be posted at the very top of your status page. There can only be 1 pinned message.
severity
: these can be info
, warning
, high
, resolved
or scheduled
. You are free to use these any way you like. The resolved
updates will be posted in green on your status page to inform the user the problem has been resolved.
time
: the date in UTC at which this status page was posted. This will get translated to your configured timezone.
Post a new update to your status page
To add a new update or message to your status page, you can make a POST
call to the /api/status-page-updates
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/status-page-updates \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"status_page_id":1, "title":"Our site is down", "text":"We are working on it!", "severity":"high", "time":"2019-09-16 10:24","pinned":true}'
As a confirmation, we'll return an HTTP/1.1 201 Created
with the received data fields.
{
"id": 66,
"title": "Our site is down",
"text": "We are working on it!",
"pinned": true,
"severity": "high",
"time": "2019-09-16 08:24:00",
"status_page_url": "https://ohdear.app/status-page/your-slug"
}
The POST
API call should contain a valid json body with the following fields.
status_page_id
: the ID of the status page where you want to add this update.
title
: the titel of your status update
text
: a longer text describing what is happening or how you're resolving the problem
severity
are: info
, warning
, high
, resolved
or scheduled
.
time
: a date field that acts as a timestamp for this update, should be in UTC in the Y-m-d H:i
format.
pinned
: whether this status page message should be pinned to the very top of the page. There can only be 1 pinned message, when set to true
it will remove the pinned
attribute from any other message.
status_page_url
: the public URL of your status page. If you have configured a custom domain, this will point to your custom domain - otherwise it'll be the URL with your status page slug on the ohdear.app domain.
Delete an update on your status page
Let's use the API to delete the status message we just posted above, the one with ID 66.
$ OHDEAR_TOKEN="your API token"
$ curl -X DELETE https://ohdear.app/api/status-page-updates/66 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
If the response code is HTTP/1.1 204 No Content
, the deletion of the status update was successful.
Maintenance windows
Retrieving maintenance periods
You can list all maintenance windows by calling the /maintenance-periods
endpoint. This can be done per website in your Oh Dear account, so you'll need to know your monitors's ID first.
In this example we'll get a list of all monitors and use the monitor ID to retrieve the maintenance periods.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"id": 1,
"url": "https://www.yoursite.tld",
"...",
}
]
}
$ curl https://ohdear.app/api/monitors/1/maintenance-periods \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"data": [
{
"id": 101,
"monitor_id": 1,
"name": "Deployment"
"starts_at": "2020-01-09 10:37:24",
"ends_at": "2020-01-09 10:42:24"
},
{
"id": 102,
"monitor_id": 1,
"name": ""
"starts_at": "2020-01-09 10:37:32",
"ends_at": "2020-01-09 10:42:32"
},
}
If there are no maintenance periods, you'll receive an empty data
array.
{
"data": []
}
If there are maintenance windows, we'll show the time of each start end end period.
{
"data": [
{
"starts_at": "2020-01-09 14:00:00",
"ends_at": "2020-01-10 14:00:00"
}
]
}
These dates are all shown in the timezone of your team. If your timezone is configured as America/Vancouver
, you'll see the dates & times shown in your own local timezone.
Creating a new maintenance period on-demand
This method applies on a per-monitor basis. If you want to put the monitor with ID 1
in maintenance right now, you can call this API method.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/start-maintenance \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
{
"id": 101,
"monitor_id": 1,
"name": "Optional name of your maintenance period"
"starts_at": "2020-01-09 10:28:54",
"ends_at": "2020-01-09 11:28:54"
}
This will start a maintenance period that, by default, will last for 60 minutes. In the response, you'll find the confirmation of the start- and end-date.
To stop the maintenance period, call the /stop-maintenance
endpoint.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/stop-maintenance \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
This will change the end-date of the currently active maintenance period to the time you've called the API.
This way, you still have a list of maintenance periods to look back on (as an audit list).
On-demand maintenance window with limited duration
If you prefer, you can also pass along the amount of seconds a maintenance period needs to be active in the /start-maintenance
API call. This can be lowered or increased depending on your needs and provides a safe fallback in case your scripts execute the start-maintenance
API call, but never get to the stop-maintenance
call.
To do so, add a stop_maintenance_after_seconds
POST value in JSON, as shown here.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/start-maintenance \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"stop_maintenance_after_seconds":"300"}'
{
"id": 101,
"monitor_id": 1,
"starts_at": "2020-01-09 10:37:32",
"ends_at": "2020-01-09 10:42:32"
}
This will create a maintenance period for 300 seconds, or 5 minutes, as confirmed by the JSON output.
Creating a maintenance period with custom start and end times
If you don't use the on-demand maintenance windows as shown above, you can create maintenance windows with a custom start- and end-date. This allows you to schedule maintenance ahead of times.
Since maintenance windows are applicable per website, you need the monitor ID of the website you want to put in maintenance.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/maintenance-periods \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"monitor_id":"1", "starts_at":"2020-02-01 14:00","ends_at":"2020-02-01 18:00"}'
{
"id": 101,
"monitor_id": "1",
"starts_at": "2020-02-01 14:00:00",
"ends_at": "2020-02-01 18:00:00"
}
The API expects the dates to be formatted as Y:m:d H:i
(no seconds) and the ends_at
needs to be further in the future than the starts_at
.
All dates should be in your local timezone, as configured in your team settings. (You don't need to convert anything to UTC, write the dates as they feel natural to you.)
Deleting a maintenance period
To delete a maintenance period, you need its ID. That can be retrieved with the /maintenance-periods API call, as shown above.
$ OHDEAR_TOKEN="your API token"
$ curl -X DELETE https://ohdear.app/api/maintenance-periods/101 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The example above deletes the maintenance period with ID 101
.
Cron Job Monitoring
Get all cron jobs for a website
Cron job monitors are attached to a website in Oh Dear. To retrieve a list of monitored cron jobs, you'll the monitor ID first.
The /api/monitors/{$monitorId}/cron-checks
endpoint lists all your cron job monitors for that particular monitor.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/cron-checks \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
As a result, you get the details for every cron job monitor attached to this monitor, together with its latest status:
{
"data": [
{
"id": 1,
"uuid": "0d210b09",
"name": "cronjob number one",
"type": "simple",
"description": "a description goes here",
"frequency_in_minutes": 15,
"grace_time_in_minutes": 5,
"cron_expression": null,
"server_timezone": "Europe/Brussels",
"ping_url": "https://ping.ohdear.app/0d210b09",
"created_at": "2020-07-28 13:27:02",
"latest_result": "pingFinished",
"latest_ping_at": "2020-11-16 09:19:40",
"human_readable_latest_ping_at": "5 minutes ago"
},
{
"id": 2,
"uuid": "8eb64808",
"name": "cronjob number two",
"type": "simple",
"description": "a description goes here",
"frequency_in_minutes": 60,
"grace_time_in_minutes": 10,
"cron_expression": null,
"server_timezone": "Europe/Brussels",
"ping_url": "https://ping.ohdear.app/8eb64808",
"created_at": "2020-07-28 13:27:02",
"latest_result": "pingFinished",
"latest_ping_at": "2020-11-16 09:19:40",
"human_readable_latest_ping_at": "5 minutes ago"
}
]
}
The response is a JSON encoded array with all the details of the cron jobs you defined to be monitored.
Result types
The latest_result
response in the API can be one of the following values.
These are values actively submitted by your application to your cron monitoring URL endpoint:
pingStarting
: we have received the "started" pingback from your application
pingFinished
: we have received the "finished" pingback from your application, indicating no errors occurred
pingFailed
: we have received the "failed" pingback from your application, indicating errors have occured
These are values we may have detected ourselves, without active participation from your application:
checkRanTooLate
: because we did not receive a new ping URL on time, we have marked the cron task as failed because it ran too late
sentCronNotExutedOnTimeNotification
: because the check didn't execute on time, we have sent the "did not run on time" notification
sentPingFailedNotification
: your application reported us that the last ping contained a failure, we have now sent a notification that the ping failed
The success states of a cronjob are determined only by the pingFinished
result. All other types indicate that we are either waiting on a ping, have received a failed ping, or have received no ping at all.
Create a simple cron job monitor
You can create a new cron job monitor using our API. You'll need the monitor ID where you want to attach the new monitor to.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/cron-checks \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "your cron job name",
"type": "simple",
"frequency_in_minutes": 10,
"grace_time_in_minutes": 15,
"description": "a description goes here"
}'
The example above will create a new cron job monitor by providing both the frequency and the gracetime. Note how the type
is set to simple
.
The response will include the Ping URL you can point your cron jobs to:
{
"id": 12,
"uuid": "ba4322cf",
"name": "your cron job name",
"type": "simple",
"description": "a description goes here",
"frequency_in_minutes": 10,
"grace_time_in_minutes": 15,
"cron_expression": "",
"server_timezone": null,
"ping_url": "https://ping.ohdear.app/ba4322cf",
"created_at": "2020-08-03 12:18:33"
}
The ping_url
should be used in your scripts to report back the status of your cron jobs to us.
Create a new cron job monitor via crontab expression
Alternatively, you can create a new cron job monitor using the crontab expression as well.
Note how in this example the type
is set to cron
instead of simple
.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/cron-checks \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "your cron job name",
"type": "cron",
"cron_expression":"* * * * *",
"grace_time_in_minutes": 10,
"description": "a description goes here",
"server_timezone": "UTC"
}'
The example above will create a new cron job monitor using the crontab expression * * * * *
to indicate it should run every minute.
The response will include the Ping URL you can point your cron jobs to:
{
"id": 10,
"uuid": "19eea41a",
"name": "your cron job name",
"type": "cron",
"description": "a description goes here",
"frequency_in_minutes": 0,
"grace_time_in_minutes": 10,
"cron_expression": "* * * * *",
"server_timezone": "UTC",
"ping_url": "https://ping.ohdear.app/19eea41a",
"created_at": "2020-08-03 12:13:53"
}
The ping_url
should be used in your scripts to report back the status of your cron jobs to us.
Bulk cron job synchronization
For our PHP-SDK, we've created a mass-update endpoint you can use to update all cron jobs for a single monitor. This will remove any cron job not explicitly in this sync API call, so it is a destructive action.
$ OHDEAR_TOKEN="your API token"
$ curl -X POST https://ohdear.app/api/monitors/1/cron-checks/sync \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"cron_checks": [
{
"name": "cron job #1",
"type": "cron",
"cron_expression":"* * * * *",
"grace_time_in_minutes": 10,
"description": "description for cron #1",
"server_timezone": "UTC"
},
{
"name": "cron job #2",
"type": "cron",
"cron_expression":"* * * * *",
"grace_time_in_minutes": 10,
"description": "description for cron #2",
"server_timezone": "UTC"
}
]
}'
The return data, if the API call was successful, is a confirmation of each cron job definition together with all its metadata.
[
{
"id": 9,
"uuid": "c1803dce",
"name": "cron job #1",
"type": "cron",
"description": "description for cron #1",
"frequency_in_minutes": null,
"grace_time_in_minutes": 10,
"cron_expression": "* * * * *",
"server_timezone": "UTC",
"ping_url": "https://ping.ohdear.app/c1803dce",
"created_at": "2020-09-03 19:20:25",
"latest_result": "pingFinished",
"latest_ping_at": "2020-11-16 09:19:40"
},
{
"id": 10,
"uuid": "40e8e39e",
"name": "cron job #2",
"type": "cron",
"description": "description for cron #2",
"frequency_in_minutes": null,
"grace_time_in_minutes": 10,
"cron_expression": "* * * * *",
"server_timezone": "UTC",
"ping_url": "https://ping.ohdear.app/40e8e39e",
"created_at": "2020-09-03 19:20:25",
"latest_result": "pingFinished",
"latest_ping_at": "2020-11-16 09:19:40"
}
]
Error handling
If an error occured, you'll be given a non-HTTP/200 response code. The resulting payload might look like this.
{
"message": "The given data was invalid.",
"errors": {
"name": ["There already exist a cron check with this name for this site."]
}
}
DNS Records
Retrieving DNS history for a monitor
To retrieve a list of your entire Dns history and issues, you'll the monitor ID first.
The /api/monitors/{$monitorId}/dns-history-items
endpoint lists all DNS related information we retrieved for your domain.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/dns-history-items \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
As a result, you get the history of the DNS records for your domain, together with all issues we found. Here's an example result for records retrieved for a https://freek.dev
.
The results are sorted in reverse chronological order: the topmost item will contain your current DNS records.
{
"data": [
{
"id": 2,
"authoritative_nameservers": [
"ns1.openprovider.nl",
"ns2.openprovider.be",
"ns3.openprovider.eu"
],
"dns_records": [
{
"host": "freek.dev",
"ttl": 1017,
"class": "IN",
"type": "A",
"ip": "138.197.187.74"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 10,
"target": "aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 20,
"target": "alt1.aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 20,
"target": "alt2.aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 30,
"target": "aspmx2.googlemail.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 30,
"target": "aspmx3.googlemail.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns1.openprovider.nl"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns2.openprovider.be"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns3.openprovider.eu"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "SOA",
"mname": "ns1.openprovider.nl",
"rname": "dns.openprovider.eu",
"serial": 2021080201,
"refresh": 10800,
"retry": 3600,
"expire": 604800,
"minimum_ttl": 3600
},
{
"host": "freek.dev",
"ttl": 1060,
"class": "IN",
"type": "TXT",
"txt": "v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all"
}
],
"raw_dns_records": {
"ns1.openprovider.nl": [
"freek.dev.\t\t1017\tIN\tA\t138.197.187.74",
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
],
"ns2.openprovider.be": [
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
],
"ns3.openprovider.eu": [
"freek.dev.\t\t1017\tIN\tA\t138.197.187.74",
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
]
},
"issues": [
{
"name": "NameserversAreNotInSync",
"nameservers": [
"ns2.openprovider.be"
]
}
],
"diff_summary": "No changes",
"created_at": "2021-11-05 16:07:38"
},
{
"id": 1,
"authoritative_nameservers": [
"ns1.openprovider.nl",
"ns2.openprovider.be",
"ns3.openprovider.eu"
],
"dns_records": [
{
"host": "freek.dev",
"ttl": 1017,
"class": "IN",
"type": "A",
"ip": "138.197.187.74"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 10,
"target": "aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 20,
"target": "alt1.aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 20,
"target": "alt2.aspmx.l.google.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 30,
"target": "aspmx2.googlemail.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "MX",
"pri": 30,
"target": "aspmx3.googlemail.com"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns1.openprovider.nl"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns2.openprovider.be"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "NS",
"target": "ns3.openprovider.eu"
},
{
"host": "freek.dev",
"ttl": 4435,
"class": "IN",
"type": "SOA",
"mname": "ns1.openprovider.nl",
"rname": "dns.openprovider.eu",
"serial": 2021080201,
"refresh": 10800,
"retry": 3600,
"expire": 604800,
"minimum_ttl": 3600
},
{
"host": "freek.dev",
"ttl": 1060,
"class": "IN",
"type": "TXT",
"txt": "v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all"
}
],
"raw_dns_records": {
"ns1.openprovider.nl": [
"freek.dev.\t\t1017\tIN\tA\t138.197.187.74",
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
],
"ns2.openprovider.be": [
"freek.dev.\t\t1017\tIN\tA\t138.197.187.74",
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
],
"ns3.openprovider.eu": [
"freek.dev.\t\t1017\tIN\tA\t138.197.187.74",
"freek.dev.\t\t1060\tIN\tTXT\t\"v=spf1 include:amazonses.com include:eu.mailgun.org include:spf.factuursturen.be include:sendgrid.net include:_spf.google.com a mx ~all\"",
"freek.dev.\t\t4435\tIN\tMX\t10\taspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt1.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t20\talt2.aspmx.l.google.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx2.googlemail.com.",
"freek.dev.\t\t4435\tIN\tMX\t30\taspmx3.googlemail.com.",
"freek.dev.\t\t4435\tIN\tNS\tns1.openprovider.nl.",
"freek.dev.\t\t4435\tIN\tNS\tns2.openprovider.be.",
"freek.dev.\t\t4435\tIN\tNS\tns3.openprovider.eu.",
"freek.dev.\t\t4435\tIN\tSOA\tns1.openprovider.nl.\tdns.openprovider.eu.\t2021080201\t10800\t3600\t604800\t3600"
]
},
"issues": [],
"diff_summary": "No changes",
"created_at": "2021-11-05 16:02:38"
}
}
Possible values of issues
The issues
property will return an array with issues we detected. Each item in that array will have a property name
that can contain on of the following values:
CouldNotDiscoverNameservers
: we couldn't find the authoritative nameservers for your domain
CouldNotFindAnyDnsRecords
: some nameservers of your domain did not return any records. These nameservers are listed in the nameservers
property of this array
NameserversAreNotInSync
: a nameserver of your domain returned different records than the other nameservers. The names of the nameservers that are not in sync are listed in the nameservers
property of this array
Application Health Monitoring
Retrieving application health check for a monitor
To retrieve a list of application health checks, you'll need the monitor ID first.
The /api/monitors/{$monitorId}/application-health-checks
endpoint lists all application health checks that are running for
your monitor.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/application-health-checks \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
This is what could get as a result:
{
"data": [
{
"id": 1,
"name": "DiskUsage",
"label": "Disk usage",
"status": "failed",
"message": "The disk is nearly full (91%)",
"short_summary": "91%",
"meta": {
"usage": 91
},
"detected_at": "2021-01-01 00:00:00",
"updated_at": "2021-01-01 00:00:00"
}
]
}
Available fields
The message
property will be filled when a check failed. It will contain the message that we also have sent via a
notification.
The short_summary
property contains a string that we'll also display on the application health check overview of your
site.
The status
property can contain one of the following values:
ok
: the check was ok
warning
: the check is closed to failing
failed
: the check did fail
crashed
: something went wrong running the check itself
skipped
: the check wasn't performed in this run
The meta
property contains an object that contain extra info that was sent along with the check result.
Retrieving the history of a health check
The /api/monitors/{$monitorId}/application-health-checks/{$applicationHealthCheckId}
endpoint lists the history of results
of an application health check.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/application-health-checks/1 \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
It will return the history of the check results. The newest items appear first in the list.
{
"data": [
{
"id": 2,
"status": "ok",
"message": "",
"short_summary": "60%",
"meta": {
"usage": 60
},
"detected_at": "2021-01-01 00:01:00",
"updated_at": "2021-01-01 00:01:00"
},
{
"id": 1,
"status": "failed",
"message": "The disk is nearly full (91%)",
"short_summary": "91%",
"meta": {
"usage": 91
},
"detected_at": "2021-01-01 00:00:00",
"updated_at": "2021-01-01 00:00:00"
}
]
}
Domain monitoring
Retrieving domain information for a site
To retrieve domain information of a site, you'll need the monitor ID first.
The /api/monitors/{$monitorId}/domain
endpoint displays domain related information we retrieved for your domain.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/domain \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
The response contains the most important dates of the domain, and the domain status. In the rdap_domain_response
key, you'll find the raw response we got from RDAP about your domain. The created_at
key contains the date on which we queried RDAP.
Here's example output when monitoring google.com:
{
"expires_at": "2028-09-14 04:00:00",
"registered_at": "1997-09-15 04:00:00",
"last_changed_at": "2019-09-09 15:39:04",
"last_updated_in_rdap_db_at": "2022-06-11 00:32:59",
"domain_statuses": {
"client delete prohibited": true,
"client transfer prohibited": true,
"client update prohibited": true,
"server delete prohibited": true,
"server transfer prohibited": true,
"server update prohibited": true
},
"rdap_domain_response": {
"links": [
{
"rel": "self",
"href": "https://rdap.verisign.com/com/v1/domain/GOOGLE.COM",
"type": "application/rdap+json",
"value": "https://rdap.verisign.com/com/v1/domain/GOOGLE.COM"
},
{
"rel": "related",
"href": "https://rdap.markmonitor.com/rdap/domain/GOOGLE.COM",
"type": "application/rdap+json",
"value": "https://rdap.markmonitor.com/rdap/domain/GOOGLE.COM"
}
],
"events": [
{
"eventDate": "1997-09-15T04:00:00Z",
"eventAction": "registration"
},
{
"eventDate": "2028-09-14T04:00:00Z",
"eventAction": "expiration"
},
{
"eventDate": "2019-09-09T15:39:04Z",
"eventAction": "last changed"
},
{
"eventDate": "2022-06-11T00:32:59Z",
"eventAction": "last update of RDAP database"
}
],
"handle": "2138514_DOMAIN_COM-VRSN",
"status": [
"client delete prohibited",
"client transfer prohibited",
"client update prohibited",
"server delete prohibited",
"server transfer prohibited",
"server update prohibited"
],
"ldhName": "GOOGLE.COM",
"notices": [
{
"links": [
{
"href": "https://www.verisign.com/domain-names/registration-data-access-protocol/terms-service/index.xhtml",
"type": "text/html"
}
],
"title": "Terms of Use",
"description": ["Service subject to Terms of Use."]
},
{
"links": [
{
"href": "https://icann.org/epp",
"type": "text/html"
}
],
"title": "Status Codes",
"description": [
"For more information on domain status codes, please visit https://icann.org/epp"
]
},
{
"links": [
{
"href": "https://icann.org/wicf",
"type": "text/html"
}
],
"title": "RDDS Inaccuracy Complaint Form",
"description": [
"URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"
]
}
],
"entities": [
{
"roles": ["registrar"],
"handle": "292",
"entities": [
{
"roles": ["abuse"],
"vcardArray": [
"vcard",
[
["version", [], "text", "4.0"],
["fn", [], "text", ""],
[
"tel",
{
"type": "voice"
},
"uri",
"tel:+1.2086851750"
],
["email", [], "text", "[email protected]"]
]
],
"objectClassName": "entity"
}
],
"publicIds": [
{
"type": "IANA Registrar ID",
"identifier": "292"
}
],
"vcardArray": [
"vcard",
[
["version", [], "text", "4.0"],
["fn", [], "text", "MarkMonitor Inc."]
]
],
"objectClassName": "entity"
}
],
"secureDNS": {
"delegationSigned": false
},
"nameservers": [
{
"ldhName": "NS1.GOOGLE.COM",
"objectClassName": "nameserver"
},
{
"ldhName": "NS2.GOOGLE.COM",
"objectClassName": "nameserver"
},
{
"ldhName": "NS3.GOOGLE.COM",
"objectClassName": "nameserver"
},
{
"ldhName": "NS4.GOOGLE.COM",
"objectClassName": "nameserver"
}
],
"objectClassName": "domain",
"rdapConformance": [
"rdap_level_0",
"icann_rdap_technical_implementation_guide_0",
"icann_rdap_response_profile_0"
]
},
"created_at": "2022-06-10 20:33:06"
}
Lighthouse SEO monitoring
To retrieve Lighthouse SEO reports of a site, you'll need the monitor ID first.
Getting the latest report
The /api/monitors/{$monitorId}/lighthouse-reports/latest
endpoint displays that latest Lighthouse report we generated for your site.
$ OHDEAR_TOKEN="your API token"
$ curl https://ohdear.app/api/monitors/1/lighthouse-reports/latest \
-H "Authorization: Bearer $OHDEAR_TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
This will return a result like
{
"id": 1,
"performance_score": 50,
"accessibility_score": 98,
"best_practices_score": 92,
"seo_score": 92,
"progressive_web_app_score": 30,
"first_contentful_paint_in_ms": 1851.45,
"speed_index_in_ms": 1866,
"largest_contentful_paint_in_ms": 1851.45,
"time_to_interactive_in_ms": 2473.87,
"total_blocking_time_in_ms": 123.77,
"cumulative_layout_shift": 0.01,
"performed_on_checker_server": "lighthouse-checker-frankfurt-1",
"json_report": {
/*
Omitted for brevity, will contain the full JSON report generated by the lighthouse tool
*/
},
"issues": [
{
"category": "performance",
"actualScore": 50,
"notificationThreshold": 70
}
],
"created_at": "2023-01-14 00:00:55"
}
Getting a list of all reports
The /api/monitors/{$monitorId}/lighthouse-reports
endpoint returns all Lighthouse SEO reports.
Here's some example output. Notice that we don't return the json_report
in the response for this endpoint.
{
"data": [
{
"id": 1423,
"performance_score": 97,
"accessibility_score": 98,
"best_practices_score": 92,
"seo_score": 92,
"progressive_web_app_score": 30,
"first_contentful_paint_in_ms": 1851.45,
"speed_index_in_ms": 1866,
"largest_contentful_paint_in_ms": 1851.45,
"time_to_interactive_in_ms": 2473.87,
"total_blocking_time_in_ms": 123.77,
"cumulative_layout_shift": 0.01,
"performed_on_checker_server": "lighthouse-checker-frankfurt-1",
"issues": [
{
"category": "performance",
"actualScore": 97,
"notificationThreshold": 99
}
],
"created_at": "2023-01-14 00:00:55"
},
{
"id": 1321,
"performance_score": 89,
"accessibility_score": 98,
"best_practices_score": 92,
"seo_score": 92,
"progressive_web_app_score": 30,
"first_contentful_paint_in_ms": 294.65,
"speed_index_in_ms": 328,
"largest_contentful_paint_in_ms": 294.65,
"time_to_interactive_in_ms": 1585.48,
"total_blocking_time_in_ms": 445.9,
"cumulative_layout_shift": 0.06,
"performed_on_checker_server": "lighthouse-checker-frankfurt-1",
"issues": [
{
"category": "performance",
"actualScore": 89,
"notificationThreshold": 99
}
],
"created_at": "2023-01-12 23:38:19"
}
],
"links": {
"first": "https://ohdear.app/api/monitors/1/lighthouse-reports?page%5Bnumber%5D=1",
"last": "https://ohdear.app/api/monitors/1/lighthouse-reports?page%5Bnumber%5D=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "https://ohdear.app/api/monitors/1/lighthouse-reports?page%5Bnumber%5D=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "https://ohdear.app/api/monitors/1/lighthouse-reports",
"per_page": 200,
"to": 2,
"total": 2
}
}
Getting a specific Lighthouse SEO report
The /api/monitors/{$monitorId}/lighthouse-reports/{$reportId}
endpoint returns a specific report. You can get the $reportId
by retrieving the list of Lighthouse reports (see above)
Here's an example response.
{
"id": 1,
"performance_score": 50,
"accessibility_score": 98,
"best_practices_score": 92,
"seo_score": 92,
"progressive_web_app_score": 30,
"first_contentful_paint_in_ms": 1851.45,
"speed_index_in_ms": 1866,
"largest_contentful_paint_in_ms": 1851.45,
"time_to_interactive_in_ms": 2473.87,
"total_blocking_time_in_ms": 123.77,
"cumulative_layout_shift": 0.01,
"performed_on_checker_server": "lighthouse-checker-frankfurt-1",
"json_report": {
/*
Omitted for brevity, will contain the full JSON report generated by the lighthouse tool
*/
},
"issues": [
{
"category": "performance",
"actualScore": 50,
"notificationThreshold": 70
}
],
"created_at": "2023-01-14 00:00:55"
}
The /api/tags
endpoint will return all tags for your team.
{
"data": [
{
"id": 22,
"team_id": 5,
"team_name": "Spatie",
"name": "production",
"slug": "production",
"created_at": "2025-08-15 09:00:00",
"updated_at": "2025-08-15 09:00:00",
"sites": [
17,
317
]
}
]
}
Creating a tag
The /api/tags
POST endpoint will create a new tag for your team with the following payload:
team_id
(required): The ID of your team to create the tag for.
name
(required): The name of the tag.
sites
(optional): An array of site IDs to associate with the tag.
{
"team_id": 5,
"name": "production",
"sites": [17,317]
}
Now you have created a tag you can assign notification destinations to it.
Tag groups
Tag groups are a way to combine multiple tags and wildcard tags into a central place to manage notifications. For example, you could create a tag group called VIP Client Production Sites
that combines the VIP
, production
and *client*
wildcard tag.
Getting all tag groups
The /api/tag-groups
endpoint will return all tag groups for your team:
{
"data": [
{
"id": 6,
"label": "VIP Client Production Sites",
"team_id": 5,
"team_name": "Spatie",
"created_at": "2025-08-15 09:00:00",
"updated_at": "2025-08-15 09:00:00",
"tags": [
{
"id": 77,
"team_id": 5,
"team_name": "Spatie",
"name": "VIP",
"slug": "vip",
"created_at": "2025-08-15 09:00:00",
"updated_at": "2025-08-15 09:00:00",
"sites": []
},
{
"id": 78,
"team_id": 5,
"team_name": "Spatie",
"name": "production",
"slug": "production",
"created_at": "2025-08-15 09:00:00",
"updated_at": "2025-08-15 10:41:13",
"sites": []
},
{
"id": null,
"team_id": 5,
"team_name": "Spatie",
"name": "*client*",
"slug": "*client*",
"created_at": null,
"updated_at": null,
"sites": []
}
]
}
],
"links": {
"first": "https://ohdear.app/api/tag-groups?page%5Bnumber%5D=1",
"last": "https://ohdear.app/api/tag-groups?page%5Bnumber%5D=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "https://ohdear.app/api/tag-groups?page%5Bnumber%5D=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "https://ohdear.app/api/tag-groups",
"per_page": 200,
"to": 2,
"total": 2
}
}
Note: the wildcard tags have a null id
property.
Creating a tag group
The /api/tag-groups
POST endpoint will create a new tag group for your team with the following payload:
label
(required): The label of the tag group.
tags
(optional): An array of tag names to associate with the tag group.
{
"team_id": 5,
"label": "VIP Client Production Sites",
"tags": ["VIP", "production", "*client*"]
}
Note: Any tags that do not exist will be created.
Updating a tag group
The /api/tag-groups/{$tagGroupId}
PUT endpoint will update an existing tag group for your team with the following payload:
label
(required): The label of the tag group.
tags
(optional): An array of tag names to associate with the tag group.
{
"label": "VIP Client Production Sites",
"tags": ["VIP", "production", "*client*", "additional-tag"],
}
Note: an empty array of tags will remove all tags from the tag group. If you just want to update the label, you should omit the tags
property.
Deleting a tag group
The /api/tag-groups/{$tagGroupId}
DELETE endpoint will delete an existing tag group for your team.
Notification destinations
Using our API, you can retrieve notification destination of your site or teams. Because the results of these API calls
can contain sensitive values, only admins and team owners are allowed to use them.
Getting the notification destinations of all your teams
The /api/team-notification-destinations
endpoint will return all notification destinations defined on the team level
for all your teams.
{
"data": [
{
"id": 123,
"team_id": 1,
"channel": "mail",
"destination": {
"mail": "[email protected]"
},
"notification_types": [
"UptimeCheckFailedNotification",
"UptimeCheckRecoveredNotification",
...
]
}
]
}
Getting the notification destinations of a site
To retrieve the notification destinations of a site, you'll need the monitor ID first.
The /api/monitors/{$monitorId}/notification-destinations
endpoint will return all notification destinations for the monitor.
{
"data": [
{
"id": 456,
"channel": "mail",
"destination": {
"mail": "[email protected]"
},
"notification_types": [
"UptimeCheckFailedNotification",
"UptimeCheckRecoveredNotification",
...
]
}
]
}
Getting the notification destinations of a tag
The /api/tags/notification-destinations
endpoint will return all notification destinations defined on the tag level
for all your teams.
{
"data": [
{
"id": 123,
"tag": {
"id": 1,
"name": "Production",
"slug": "production"
},
"team_id": 1,
"channel": "mail",
"destination": {
"mail": "[email protected]"
},
"notification_types": [
"UptimeCheckFailedNotification",
"UptimeCheckRecoveredNotification",
...
]
}
]
}
Getting the notification destinations of a tag group
The /api/tag-groups/{$tagGroupId}/notification-destinations
endpoint will return all notification destinations defined on the tag group level
for all your teams.
{
"data": [
{
"id": 158,
"tagGroup": {
"id": 1,
"label": "VIP Client Production Sites",
"team_id": 5,
"team_name": "Spatie",
"created_at": "2025-08-01 09:00:00",
"updated_at": "2025-08-01 09:00:00",
"tags": [
{
"id": 72,
"team_id": 5,
"team_name": "Spatie",
"name": "VIP",
"slug": "vip",
"created_at": "2025-08-01 09:00:00",
"updated_at": "2025-08-01 09:00:00",
"sites": []
},
...
]
},
"channel": "slackApi",
"destination": {
"channel": "#monitoring",
"slack_api_token": "xoxp-2..."
},
"notification_types": [
"UptimeCheckFailedNotification",
"UptimeCheckRecoveredNotification",
]
}
]
}
Creating notification destinations
You can create a new notification destination for a monitor or team. The required payload is in the same format as the API to retrieve notification destinations which makes it easy to copy notification configurations across monitors and teams.
monitors: /api/monitors/{$monitorId}/notification-destinations
Teams: /api/team-notification-destinations/{$teamId}
Tags: /api/tags/notification-destinations/{$tagId}
Tag groups: /api/tag-groups/{$tagGroupId}/notification-destinations
Payload
{
"channel": "mail",
"destination": {
"mail": "[email protected]"
},
"notification_types": [
"UptimeCheckRecoveredNotification",
"UptimeCheckFailedNotification",
]
}
Note: an empty array will create your configuration but will not respond to any Oh Dear events. Make sure the array has at least one item.
Payload (all events)
You can accept all supported notification types by removing the notification_types
array from your request.
{
"channel": "mail",
"destination": {
"mail": "[email protected]"
}
}
Sending a test notification
You can also trigger a test notification as part of the creation process by adding test: true|false
to you request body. By default this will be false
.
{
"test": true,
"channel": "mail",
"destination": {
"mail": "[email protected]"
}
}
Updating notification destinations
You can update an existing notification destination by sending a PUT/PATCH
request with the updated payload to the following endpoints:
monitors: /api/monitors/{$monitorId}/notification-destinations/{$destinationId}
Teams: /api/team-notification-destinations/{$teamId}/destination/{$destinationId}
Tags: /api/tags/notification-destinations/{$tagId}/destination/{$destinationId}
Tag groups: /api/tag-groups/{$tagGroupId}/notification-destinations/{$destinationId}
{
"test": true,
"channel": "mail",
"destination": {
"mail": "[email protected]"
}
}
You can also send test notifications on updates by adding test: true
to your request body. By default this will be false
.
Deleting notifications destinations#
You can delete a notification destination by sending a DELETE
request to the following endpoints:
monitors: /api/monitors/{$monitorId}/notification-destinations/{$destinationId}
Teams: /api/team-notification-destinations/{$teamId}/destination/{$destinationId}
Tags: /api/tags/{$tagId}/notification-destinations/destination/{$destinationId}
Tag groups: /api/tag-groups/{$tagGroupId}/notification-destinations/{$destinationId}
No other parameters are required and a 204 response will be returned if the deletion was successful.
Channels and destinations
Each channel requires a valid destination object. You can find the supported channels and their config objects below.
Email
{
"channel": "mail",
"destination": {
"mail": "[email protected]"
},
}
Discord
{
"channel": "discord",
"destination": {
"url": "https://discord.com/api/webhooks/your-webhook-url"
}
}
Microsoft Teams
{
"channel": "microsoftTeams",
"destination": {
"url": "https://teams.microsoft.com/webhook/your-webhook-url"
}
}
Pushover
{
"channel": "pushover",
"destination": {
"userKey": "your-user-key",
"apiToken": "your-api-token",
"priority": "0"
}
}
Priority [lowest - emergency]: -2
, -1
, 0
, 1
, 2
Slack
{
"channel": "slack",
"destination": {
"url": "https://hooks.slack.com/services/your-webhook-url"
}
}
Slack API
{
"channel": "slackApi",
"destination": {
"token": "xoxp-rest-of-your-token-here",
"channel": "#general"
}
}
Telegram
{
"channel": "telegram",
"destination": {
"chat_id": "your-chat-id",
}
}
SMS
{
"channel": "sms",
"destination": {
"apiKey": "your-vonage-api-key",
"apiSecret": "your-vonage-api-secret",
"from": "your-sender-number",
"to": "your-recipient-number"
}
}
Webhook
{
"channel": "webhooks",
"destination": {
"url": "https://your-webhook-url"
}
}
Opsgenie
{
"channel": "opsgenie",
"destination": {
"apiKey": "your-api-key",
"euEndpoint": true,
"priority": "P3"
}
}
Priority: P1
, P2
, P3
, P4
, P5
Pager Duty
{
"channel": "pagerDuty",
"destination": {
"apiKey": "your-service-key",
"serviceId": "your-service-id",
"fromEmail": "[email protected]"
}
}
Notification types
The following notification types are valid for both teams and monitors:
- ApplicationHealthClientErrorNotification
- ApplicationHealthProblemDetectedNotification
- ApplicationHealthProblemFixedNotification
- ApplicationHealthResultsTooOldNotification
- BrokenLinksFoundNotification
- BrokenLinksFixedNotification
- CertificateExpiresSoonNotification
- CertificateFixedNotification
- CertificateHasChangedNotification
- CertificateUnhealthyNotification
- CronFailedNotification
- CronNotExecutedOnTimeNotification
- DnsIssuesFoundNotification
- DnsIssuesFixedNotification
- DnsRecordsChangedNotification
- DomainIssuesFoundNotification
- DomainIssuesFixedNotification
- LighthouseIssuesDetectedNotification
- LighthouseIssuesFixedNotification
- MixedContentFoundNotification
- MixedContentFixedNotification
- PerformanceDeltaExceededNotification
- PerformanceThresholdExceededNotification
- PerformanceThresholdRecoveredNotification
- SitemapIssuesFoundNotification
- SitemapIssuesFixedNotification
- UptimeCheckFailedNotification
- UptimeCheckRecoveredNotification
Team notification types
In addition to the generic notification types listed above the following are also valid for teams only:
<<<<<<< HEAD
- MonitorAddedNotification
=======
- SiteAddedNotification
main