---
title: Update a site
description: Updates an uptime monitoring site.
url: https://docs.honeybadger.io/api/v3/uptime-sites/update-site/
---

API v3 is in preview

Endpoints and response shapes [can change before general availability](https://docs.honeybadger.io/api/v3/#preview). API v2 is unchanged.

PATCH `https://app.honeybadger.io/v3/projects/{project_id}/sites/{site_id}`

[Required scope](https://docs.honeybadger.io/api/v3/authentication/#scopes) `uptime:write`

Updates an uptime monitoring site.

Example request

```sh
curl -X PATCH "https://app.honeybadger.io/v3/projects/$PROJECT_ID/sites/$SITE_ID" \
  -H "Authorization: Bearer $HONEYBADGER_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://www.example.com/health"
}'
```

## Parameters

### Path parameters

| Field | Type | Description |
| - | - | - |
| `project_id` required | string | Project ID. |
| `site_id` required | uuid | Site identifier (uuid). |

## Request body

Omitted fields are unchanged; null resets or clears the fields marked nullable. Read-only fields are ignored.

| Field | Type | Description |
| - | - | - |
| `name` | string | Name shown for the site in Honeybadger and on status pages. |
| `url` | uri | Must start with http\:// or https\://, with no surrounding whitespace. |
| `match_type` | string \| null | How a check decides the site is up (see SiteMatchType). Null resets it to success. How a response is judged. `success`: any 2xx; `match` must be left out. `exact`: the response status code equals `match`. `include` / `exclude`: the body does / doesn't contain `match`. `jmespath`: `match` is a JMESPath expression evaluated against the JSON body; numbers in it use backticks, not quotes. Allowed values: `success`, `exact`, `include`, `exclude`, `jmespath`. Default: `"success"`. |
| `match` | string \| null | What `match_type` compares against (see SiteMatchType). Required unless `match_type` is `success`, and must be left out with `success`. Null clears it. Max length 255. |
| `request_method` | string \| null | HTTP method each check sends. Null resets it to GET. Allowed values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. Default: `"GET"`. |
| `validate_ssl` | boolean \| null | Fail the check when the TLS certificate does not validate. Null resets it to true. Default: `true`. |
| `request_body` | string \| null | Body to send with the request, up to 32 KB. Null clears it. Max length 32768. |
| `request_headers` | object \| null | Headers to send with the request, as header name to value. Replaces the stored headers; null or `{}` clears them. Default: `{}`. |
| `request_headers.*` | string | Any other keys. |
| `frequency` | integer \| null | Minutes between checks. The plan sets the most frequent allowed. Null resets it to 5. A value below the plan's floor is refused. Allowed values: `1`, `2`, `5`, `15`. Default: `5`. |
| `locations` | array \| null | A set of location names to check from. Null or empty means every location. A set; order is ignored and duplicates are dropped. Allowed values: `Virginia`, `Frankfurt`, `Singapore`, `London`, `Oregon`. Default: `[]`. |
| `outage_threshold` | integer \| null | Consecutive failures before an outage is declared. null means down once failures reach 50% of locations. Min 1. |
| `active` | boolean \| null | Whether monitoring is active. After 740 consecutive failed checks the server sets it to false. Null resets it to true. Default: `true`. |
| `timeout` | integer \| null | Request timeout in seconds, 1 to 120, default 30, and less than the frequency in seconds. Changing it requires the uptime timeout feature on the account; without it, only the current value (on create, the default) is accepted. Null resets it to the default. |

## Responses

### 200 Success

Example payload

```json
{
  "data": {
    "id": "9f8b6d2e-4c1a-4b7f-9e35-2a6c8d0f1b47",
    "project_id": "Xk9mZp",
    "name": "Production App",
    "url": "https://app.example.com",
    "active": true,
    "state": "up",
    "last_checked_at": "2026-01-01T00:00:00Z",
    "match_type": "success",
    "match": null,
    "request_method": "GET",
    "frequency": 1,
    "locations": [
      "Virginia",
      "London"
    ],
    "validate_ssl": true,
    "request_headers": {},
    "request_body": null,
    "outage_threshold": 2,
    "timeout": 30,
    "created_at": "2026-01-01T00:00:00Z",
    "links": {
      "web": "https://app.honeybadger.io/projects/Xk9mZp"
    }
  },
  "meta": {
    "request_id": "6f1c2b0e-8d4a-4f6e-9b2a-3c5d7e9f1a2b"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `data` required | object | A URL Honeybadger checks for uptime from several locations on a schedule, alerting when it goes down and when it recovers. |
| `data.id` required | uuid | |
| `data.project_id` required | string | ID of the project this site belongs to. |
| `data.name` required | string | Display name for the site. |
| `data.url` required | string | URL to monitor. Includes the scheme. |
| `data.active` required | boolean | Whether monitoring is active. After 740 consecutive failed checks the server sets it to false. |
| `data.state` required | string | Current state of the site. Allowed values: `pending`, `up`, `down`. |
| `data.last_checked_at` required | date-time \| null | When the site was last checked. |
| `data.match_type` required | string | How a response is judged. `success`: any 2xx; `match` must be left out. `exact`: the response status code equals `match`. `include` / `exclude`: the body does / doesn't contain `match`. `jmespath`: `match` is a JMESPath expression evaluated against the JSON body; numbers in it use backticks, not quotes. Allowed values: `success`, `exact`, `include`, `exclude`, `jmespath`. |
| `data.match` required | string \| null | Value to match in the response. |
| `data.request_method` required | string | Allowed values: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`. |
| `data.frequency` required | integer | Minutes between checks. A value below the plan's floor is refused. Allowed values: `1`, `2`, `5`, `15`. |
| `data.locations` required | array\<string> | Locations checks run from. Unordered. \[] means every location. Allowed values: `Virginia`, `Frankfurt`, `Singapore`, `London`, `Oregon`. |
| `data.validate_ssl` required | boolean | Whether to validate SSL certificates. |
| `data.request_headers` required | object | Custom headers sent with check requests, as header name to value; {} when there are none. |
| `data.request_headers.*` | string | Any other keys. |
| `data.request_body` required | string \| null | Request body for POST/PUT checks. |
| `data.outage_threshold` required | integer \| null | Number of failed checks before declaring an outage. |
| `data.timeout` required | integer \| null | The stored request timeout in seconds; null means the default, 30. On a plan without custom timeouts the default applies whatever is stored. |
| `data.created_at` required | date-time | When the site was created. |
| `data.links` required | object | Links into the Honeybadger web UI. |
| `data.links.web` required | uri | The resource's page in the Honeybadger web UI, as an absolute URL. Resources without a page, such as outages and deploys, don't have `links`. For a project provisioned through Heroku, error, notice and comment links go through Heroku's single sign-on. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

## Errors

### 401 Unauthorized

The credential is missing, invalid, expired or revoked (`unauthorized`), or was sent in a way v3 doesn’t accept (see the examples).

Example payload: unauthorized (Missing, malformed, expired or revoked credential)

```json
{
  "error": {
    "code": "unauthorized",
    "message": "Access denied"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: unsupported\_auth\_scheme (Sent as HTTP basic, which v3 does not accept)

```json
{
  "error": {
    "code": "unsupported_auth_scheme",
    "message": "Send the credential as `Authorization: Bearer <token>`; v3 does not accept HTTP basic"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: credential\_in\_query (Sent as the \`auth\_token\` query parameter)

```json
{
  "error": {
    "code": "credential_in_query",
    "message": "Credentials must be sent in the Authorization header"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

### 403 Forbidden

The credential isn’t allowed to do this. `error.code` says why. An account or project the credential doesn’t cover is 404 `not_found`, not 403.

Example payload: access\_denied (The credential isn't allowed to do this to this record)

```json
{
  "error": {
    "code": "access_denied",
    "message": "Access denied"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: insufficient\_scope (The credential lacks the permission this endpoint requires)

```json
{
  "error": {
    "code": "insufficient_scope",
    "message": "Insufficient scope",
    "details": {
      "required_scope": "faults:write",
      "token_scopes": [
        "faults:read",
        "insights:read"
      ]
    }
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: requires\_user\_token (The endpoint records who acted, and an account token has nobody)

```json
{
  "error": {
    "code": "requires_user_token",
    "message": "This endpoint records the person who acted, so it requires a user credential rather than an account token"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Example payload: project\_restricted (A project-restricted credential cannot create a project)

```json
{
  "error": {
    "code": "project_restricted",
    "message": "Project-restricted tokens cannot create projects"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

### 404 Not Found

Resource not found

Example payload

```json
{
  "error": {
    "code": "not_found",
    "message": "Resource not found"
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

### 422 Unprocessable Content

Validation error

Example payload

```json
{
  "error": {
    "code": "validation_error",
    "message": "Name can't be blank",
    "details": [
      {
        "field": "name",
        "message": "can't be blank"
      }
    ]
  },
  "meta": {
    "request_id": "abc123"
  }
}
```

Response schema

| Field | Type | Description |
| - | - | - |
| `error` required | object | |
| `error.code` required | string | Machine-readable code. Branch on this, not the message. Allowed values: `unauthorized`, `unsupported_auth_scheme`, `credential_in_query`, `access_denied`, `insufficient_scope`, `requires_user_token`, `project_restricted`, `account_inactive`, `account_parked`, `feature_unavailable`, `not_found`, `validation_error`, `delete_failed`, `limit_reached`, `rate_limit_exceeded`, `concurrent_request`, `streams_provisioning`, `ingestion_key_not_accepted`, `maintenance_mode`, `invalid_parameter`, `forbidden_attributes`, `service_unavailable`, `ambiguous_account`, `fault_merged`. |
| `error.message` required | string | Human-readable explanation. |
| `error.details` | array\<object> \| object | A list of `{field, message}` on `validation_error` and `invalid_parameter`, and on `feature_unavailable` or `limit_reached` when one field caused the refusal; `{required_scope, token_scopes}` on `insufficient_scope`; `{merged_into, location}` on `fault_merged`. Absent when there's nothing to add. |
| `meta` | object | |
| `meta.request_id` | string | Unique identifier for the request, for correlating with support. |

---

## Try Honeybadger for FREE

Intelligent logging, error tracking, and Just Enough APM™ in one dev-friendly platform. Find and fix problems before users notice.

[Start free trial](https://app.honeybadger.io/users/sign_up)

[See plans and pricing](https://www.honeybadger.io/plans/)
