-
SEO Learning Center
Broaden your SEO knowledge with resources for all skill levels.
-
The Beginner's Guide to SEO
If you're brand new to SEO, start here.
-
SEO Q&A
Get answers from the Moz Community.
-
Free Downloads and More
Quick access to whitepapers, reports, guides, webinars, and case studies.
-
Help Hub
Learn how to use Moz products.
-
Community & Events
Connect with over 600k online marketers.
-
SEO Training
Instructor-led classes and seminars
Webhooks
Webhooks let Moz tell your systems when something changes in your account, so you don't have to poll the API to find out. When an event happens, Moz sends an HTTP POST request describing it to a URL you choose. This guide explains who can use webhooks, which events are available, how to get set up, what each request looks like, and how to verify and respond to it.
Moz webhooks follow the Standard Webhooks specification, so you can verify them with any of its open source libraries.
Availability
Webhooks are currently available to Moz Local Enterprise customers only. If you would like to use webhooks, or have questions about which events would suit your business, contact our Enterprise sales team.
Supported Events
Webhooks currently cover the locations in your Moz Local account:
- local.location.created: A new location is added to your account and set up in Moz Local.
- local.location.updated: Moz detects that any of the location's details returned by the API have changed. This includes changes to its status, other than to canceled or closed.
- local.location.canceled: The location's status changes to canceled, for example through Cancel Location.
- local.location.closed: The location's status changes to closed, for example through Close Location. Changing only the location's opening status, such as marking it temporarily closed, sends local.location.updated instead.
Moz sends events when it processes a change to a location, not at the exact moment the change is made, so there can be a delay between the two. Each time Moz processes a change it sends at most one event, so several changes made close together can arrive as a single event. For example, canceling a location sends local.location.canceled, not also local.location.updated.
You may occasionally receive local.location.updated when none of the details you use have changed, for example after we add a new field to the API. Treat each event as a prompt to look the location up again, as described in Fetching the Latest Data.
Webhooks only cover changes made after your webhook is set up. To load the locations you already have, use List Locations.
You can subscribe to individual events, or to a group of events by the start of their name: subscribing to local.location or local sends you every location event, including any that are added in the future. Use complete parts of the name, such as local.location: matching is by the start of the name only, so local.location.c would match created, canceled and closed. Names are also case-sensitive, so a subscription to Local.location is not accepted. If more than one of your subscriptions matches an event, your URL still receives it once. If you register more than one URL, each one receives its own copy, with its own webhook-id.
Setting Up Webhooks
Webhooks are configured by the Moz team. To set one up, contact your Moz account manager with:
- The email address of your Moz account.
- The URL that should receive webhooks. It must use https and a public hostname (not an IP address), and be at most 255 characters long.
- The events you want to receive, or local for all Moz Local events.
We will send you the signing secret for your URL, which starts with whsec_. Store it as securely as you would a password: it is how you confirm that a request really came from Moz.
To change your URL or events, replace your signing secret, or turn deliveries off, contact your account manager:
- When you replace a secret, the old and new secrets can both be active for a while, so you can deploy the new one without rejecting any webhooks.
- When you change your URL, webhooks still waiting to be delivered, including retries, go to the new URL.
- While deliveries are turned off, new events are not saved for your URL, and they are not sent when deliveries are turned back on. Webhooks that were already waiting to be delivered are still sent.
The Webhook Request
Each webhook is a POST request with a JSON body:
POST /webhooks/moz HTTP/1.1 content-type: application/json webhook-id: msg_b9c17fcf-a201-40c0-8781-bd59d587c892 webhook-timestamp: 1792677603 webhook-signature: v1,4glDNIwsBlTNLL5vWeLkq+1mtbHbzbnH7NfV66gdo0I= {"type":"local.location.updated","timestamp":"2026-10-22T14:00:00.000Z","data":{"location_id":12345}}
Headers
- webhook-id: A unique identifier for the event, starting with msg_. It stays the same when a webhook is retried, so you can use it to recognize webhooks you have already processed.
- webhook-timestamp: When this delivery attempt was made, in Unix seconds. A retried webhook has a new timestamp.
- webhook-signature: One or more signatures of the request, separated by spaces, that prove it came from Moz. See Verifying Webhooks.
- content-type: Always application/json.
Body
Formatted for readability, the body looks like this:
{ "type": "local.location.updated", "timestamp": "2026-10-22T14:00:00.000Z", "data": { "location_id": 12345 } }
- type: The event, one of the supported events.
- timestamp: When Moz recorded the event, in ISO 8601 format and UTC. It stays the same on every retry, unlike the webhook-timestamp header, which records when each delivery attempt was made.
- data.location_id: The ID of the location the event is about. This is the same id that the Moz Local API returns for the location.
Fetching the Latest Data
A webhook tells you that a location changed, not what changed. Webhooks can arrive late or out of order, so a copy of the location's details inside the webhook could already be out of date by the time you process it. Instead, look the location up when you receive a webhook, using Lookup Location:
curl -X POST https://api.moz.com/jsonrpc \ -H "x-moz-token: <YOUR_MOZ_TOKEN>" \ -H "Content-Type: application/json" \ -d ' { "jsonrpc": "2.0", "id": "9f56d404-3e12-4822-9c5c-8197657d7be3", "method": "local.location.lookup", "params": { "data": { "id": 12345 } } }'
Verifying Webhooks
Anyone can send a request to your webhook URL, so check the signature of every request before acting on it. To verify a request:
- Join the webhook-id header, the webhook-timestamp header and the raw request body with periods, as webhook-id.webhook-timestamp.body.
- Take the part of your signing secret after whsec_ and base64-decode it. Use the result as the key to compute an HMAC-SHA256 of the joined content, then base64-encode the HMAC.
- Compare your result with each signature in the webhook-signature header, using a constant-time comparison. Each signature is a version, a comma, and the signature itself, such as v1,4glDNIws.... The request is genuine if any v1 signature matches. There can be more than one while a secret is being replaced.
- Reject the request if webhook-timestamp is more than five minutes away from your current time. This stops an intercepted request from being replayed later.
Always verify the raw request body exactly as you received it. Parsing the JSON and serializing it again can change the bytes and make the signature fail.
The Standard Webhooks libraries do all of this for you. For example, in Node.js with Express and the standardwebhooks package:
import express from "express"; import { Webhook } from "standardwebhooks"; const app = express(); // Your signing secret, including the "whsec_" prefix. const webhook = new Webhook(process.env.MOZ_WEBHOOK_SECRET); app.post( "/webhooks/moz", // Keep the raw body, since the signature covers its exact bytes. express.raw({ type: "application/json" }), async (req, res) => { let event; try { event = webhook.verify(req.body, req.headers); } catch (error) { return res.status(400).send("Invalid webhook signature"); } try { // Store the event, for example in a queue or database, and process it // separately. Moz stops retrying a webhook once you return a 2xx. await saveEvent(req.headers["webhook-id"], event); } catch (error) { // Any response other than a 2xx makes Moz retry the webhook later. return res.sendStatus(500); } res.sendStatus(204); } );
Libraries for other languages, including Python, Go, Ruby, PHP, Java and C#, are listed in the Standard Webhooks repository.
Responding to Webhooks
Respond with any 2xx status code to confirm that you received a webhook. Once Moz receives a 2xx it stops retrying that webhook, so store it before you respond. Moz waits up to 15 seconds for a response, so do any slow processing after you have responded.
Any other outcome counts as a failed delivery, including:
- A 3xx redirect. Redirects are not followed, so make sure your URL is the final destination.
- A 4xx or 5xx status code.
- No response within 15 seconds, or a connection error.
Retries
Each webhook gets up to six delivery attempts. The first is made shortly after Moz records the event, and each retry waits longer after the previous attempt:
- 2nd attempt: 5 minutes later
- 3rd attempt: 30 minutes later
- 4th attempt: 2 hours later
- 5th attempt: 8 hours later
- 6th attempt: 24 hours later
Together these cover about a day and a half. If all six attempts fail, Moz stops retrying the webhook. If your URL was unavailable for longer than that, look your locations up with List Locations to catch up on what changed.
Duplicates and Ordering
- The same webhook can arrive more than once, for example when a network problem stops your response from reaching Moz. Keep track of the webhook-id of each webhook you process and skip any you have already handled.
- Webhooks are not guaranteed to arrive in the order the events happened, especially when retries are involved. Look the location up to get its current state rather than relying on the order in which webhooks arrive.
Questions
For help setting up webhooks or with your integration, contact your Moz account manager. To talk about Moz Local Enterprise, or to request webhook events that aren't available yet, contact our Enterprise sales team.