If your WordPress plugin makes HTTP calls, I’d track four things right away: where requests go, how long they take, whether they fail, and what triggered them. That alone helps me spot timeouts, blocked domains, bad responses, and slow third-party services before they turn into user-facing problems.
Here’s the short version:
- I use WordPress HTTP hooks and essential plugins to watch outbound calls
- I treat outgoing HTTP and incoming REST traffic as two different paths
- I log URL, method, status code, runtime, source, and cleaned error details
- I mask secrets like tokens, API keys, passwords, cookies, and nonces before saving anything
- I use Query Monitor for live checks and REST logging tools for route history
- I store logs in a custom table or external log service if I need search, filters, and retention
- I prune old logs, skip noisy hosts, and watch for repeat failures like cURL error 28 timeouts
A few numbers matter here. WordPress often uses a 5-second timeout for HTTP requests. That means even 3 stalled calls can add up to 15 seconds of delay in a bad flow. And if I keep raw bodies for every request, log size can grow fast, so I cut large payloads to about 2 KB and delete old rows after 30 days.
The main idea is simple: monitor less, but monitor the right things. I want enough detail to fix errors fast without filling my database, slowing my site, or storing secret data by mistake.
From there, the article walks through the setup: hooks first, tools second, then structured logs and alerts.
Debugging WordPress with Query Monitor Plugin
sbb-itb-77ae9a4
Step 1: Set up basic monitoring with WordPress HTTP API hooks
Start with the built-in WordPress HTTP API hooks. They let you see outbound requests without changing every plugin or theme that makes them. A simple setup works well here: use pre_http_request to inspect or stop calls before they leave the site, and use http_api_debug to record what came back. If you later want to review trends over time, move that data into a custom table.
Log outgoing requests with http_api_debug
http_api_debug gives you the response or WP_Error result, the request arguments, the transport used, and the target URL [5][6]. That’s enough to build a useful request log.
If you also want response time, record microtime(true) in pre_http_request and subtract it inside http_api_debug.
// In your mu-plugin file add_filter( 'pre_http_request', function( $preempt, $args, $url ) { // Use a unique key to avoid overwriting concurrent calls to the same URL $request_key = $url . '_' . uniqid( '', true ); $GLOBALS['api_monitor_start'][ $request_key ] = microtime( true ); $GLOBALS['api_monitor_keys'][ $url ] = $request_key; return $preempt; }, 10, 3 ); add_action( 'http_api_debug', function( $response, $context, $transport, $args, $url ) { $request_key = $GLOBALS['api_monitor_keys'][ $url ] ?? null; $start = $request_key ? ( $GLOBALS['api_monitor_start'][ $request_key ] ?? microtime( true ) ) : microtime( true ); $runtime = round( microtime( true ) - $start, 3 ); $method = strtoupper( $args['method'] ?? 'GET' ); if ( is_wp_error( $response ) ) { $status_code = null; $error_message = $response->get_error_message(); } else { $status_code = wp_remote_retrieve_response_code( $response ); $error_message = null; } api_monitor_write_log( $url, $method, $status_code, $error_message, $runtime, $args, $response ); }, 10, 5 );
One small thing matters a lot: always check is_wp_error( $response ) before reading the status code. That’s how you catch failed requests like timeouts or DNS errors instead of treating them like normal HTTP responses [2][6].
And if you save request arguments or response data, clean out secrets first. Redact fields like authorization, x-api-key, access_token, nonce, and password before anything hits your logs [1].
Inspect or block risky calls with pre_http_request
You can use that same hook path to stop calls you don’t expect. Since pre_http_request is a filter, returning anything other than false prevents the request from being sent. That makes it a solid place to check the target URL against an allowlist [1].
add_filter( 'pre_http_request', function( $preempt, $args, $url ) { $allowed_domains = [ 'api.stripe.com', 'api.mailgun.net', 'www.googleapis.com' ]; $host = parse_url( $url, PHP_URL_HOST ); if ( ! in_array( $host, $allowed_domains, true ) ) { error_log( '[API Monitor] Blocked unexpected domain: ' . $host ); return new WP_Error( 'blocked_domain', 'Request blocked by API monitor allowlist.' ); } return $preempt; }, 10, 3 );
This is a clean way to catch odd traffic early. If some plugin tries to call an unknown host, the request never leaves your server [1].
Store logs in a custom table
File logging is fine at first, but a custom table makes slow and failed requests much easier to sort, filter, and review. Use $wpdb and dbDelta on plugin activation so WordPress can create or update the schema safely [1].
function api_monitor_create_table() { global $wpdb; $table = $wpdb->prefix . 'api_monitor_log'; $charset = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $table ( id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, url TEXT NOT NULL, method VARCHAR(10) NOT NULL, request_status VARCHAR(20) NOT NULL, runtime DECIMAL(10,3) NOT NULL DEFAULT 0.000, request_args MEDIUMTEXT DEFAULT NULL, response MEDIUMTEXT DEFAULT NULL, date_added DOUBLE NOT NULL, PRIMARY KEY (id), KEY runtime (runtime), KEY date_added (date_added) ) $charset;"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta( $sql ); }
A few details make this setup easier to work with:
- Store the timestamp in UTC when writing each row, then convert it for display in the admin UI [1].
- Add indexes for
runtimeanddate_addedso searches for slow requests stay efficient, such asSELECT * FROM wp_api_monitor_log WHERE runtime > 2.0[1]. - Use WP-Cron to delete logs older than 30 days.
That gives you a simple monitoring base: inspect requests before send, log what comes back, and keep the data in a format you can query later.
Step 2: Inspect API traffic with developer tools and logging plugins

WordPress API Monitoring: Tools & Storage Options Compared
Developer tools and logging plugins help you see what your hooks pick up and what slips past them. During development, use them to check request behavior before you set up long-term logs.
Use Query Monitor to find slow or failing HTTP requests
Query Monitor includes a dedicated HTTP API Requests panel that tracks WordPress HTTP API calls. For each request, it records the URL, HTTP method, response code, time taken, and the Component behind the call [7][3].
It also shows a call stack for each request, which makes it much easier to trace the exact function behind an odd or repeated API call [7][3]. In April 2026, Query Monitor 4.0 added a timeline view that shows when HTTP requests fire during the page lifecycle, alongside database queries and other events [7].
For most day-to-day debugging in development, Query Monitor does the job well. But if you’re checking incoming endpoint traffic, you’ll want a logging tool that keeps the full request and response body.
Use REST API logging tools for incoming route activity
WP REST API Log (wp-rest-api-log) records the HTTP method, response status, source, and full request/response body for every call that hits your endpoints [4]. Use it when you need a persistent, searchable history of incoming traffic [4].
Compare tools by traffic type and captured data
Use the table below to match each tool to the traffic you need to inspect.
| Tool | Traffic Type | Best For | Key Data Captured |
|---|---|---|---|
| Query Monitor | Outgoing HTTP + Incoming REST | Live debugging during development | URL, component, time, call stack, status code [7][3] |
| WP REST API Log | Incoming REST | Auditing & webhook history | Method, status, source, full request/response body [4] |
After debugging, you can turn what you found into structured logs and alerts.
Step 3: Add structured logging and alerts in your plugin
Once you’ve used developer tools to see how your API traffic behaves, the next move is to bake repeatable logging into your plugin.
Log request and response data in a structured format
After you’ve debugged the request flow, keep that same data in logs you can search later. Hook-based monitoring helps you see a request in the moment. Structured logs let you search that history over time.
Use structured JSON logs instead of manual error_log() calls. JSON is machine-readable, which means log tools can index and query it automatically [8].
Each log entry should include the same core fields every time:
timestamp(ISO 8601 format)request_id(to link related events)endpointmethodstatus_codeduration_msuser_iderror_messageif the call fails [8][1]
It also helps to add a request_source field so you know where the call came from, like a cron job, an AJAX handler, or a REST route. That small detail can save a lot of time when you’re tracking down a problem later [1].
Before you store anything, redact sensitive data. Following WordPress development best practices for secure sites, that includes tokens, keys, cookies, passwords, and nonces [1]. You should also truncate large request and response bodies to about 2 KB so your logs don’t balloon in size [1].
Choose between file, database, and external monitoring storage
Once your log format is set, the next choice is where to store those records. The right option depends on traffic, how often you need to search logs, and how long you plan to keep them.
| Storage Type | Overhead | Searchability | Best for production | Maintenance |
|---|---|---|---|---|
File-based (debug.log) |
Low (if rotated) | Poor (requires grep or tail) |
Low (risk of disk fill) | High (manual cleanup) |
| Database (Custom Table) | Moderate | High (via SQL/WP Admin) | Medium (can impact DB performance) | Medium (requires pruning cron) |
| External (APM/Log SaaS) | Minimal (offloaded) | Excellent (dashboards/filters) | High (best for scale) | Low (managed by provider) |
For high-traffic sites, sending logs to an external service usually works better than trying to manage local storage and cleanup on your own.
Best practices: keep monitoring useful, safe, and lightweight
Cut performance overhead and control log growth
Once logging is set up, the job changes. Now you need to keep it lean.
The main problem is simple: too much logging. One plugin action can fire off dozens of outbound requests. If you log every single one, storage fills up fast.
A better move is selective logging. Skip high-volume, low-risk hosts like api.wordpress.org and secure.gravatar.com. For low-priority calls, such as analytics pings and tracking pixels, use short 2–3 second timeouts. WordPress uses a 5-second timeout by default, so three stalled requests can stack up to 15 seconds of latency for a real visitor [9].
Keep retention short, and prune logs on a set schedule.
After you cut log volume, stop staring at raw traffic counts. Look for failure patterns instead. That’s usually where the useful signal lives.
Review security signals and retest after updates
Once logging is running, treat it like an early warning system, not just a debugging aid. Watch for repeated 4xx/5xx responses, SSL errors, and connection refused messages. Those signs often point to a misconfigured integration or an API provider outage [2].
Any time you update WordPress core, a plugin, or your hosting setup, retest your hooks and spot-check your logs.
Used this way, monitoring stays helpful without turning into another source of overhead.
Key takeaways
Keep monitoring selective, redacted, and short-lived. Log only what helps you diagnose failures, then prune the rest.
FAQs
How do I track API request time in WordPress?
To track API request time in WordPress, use diagnostic tools that log execution time and response duration.
- Inspect HTTP Requests records runtime for outgoing HTTP calls.
- HTTP Requests Manager shows timing metrics and backtraces.
- Query Monitor displays HTTP API and REST API request durations in the admin toolbar.
- WP Debug Toolkit CLI profiles REST API endpoints with an execution-time breakdown.
What should I redact before logging API requests?
Always redact sensitive information before logging API requests in your WordPress plugin. That means authentication tokens, API keys, user passwords, and personally identifiable information (PII).
Before you write request or response payloads to logs or database tables, sanitize them first. Keep the parts that help with debugging – like the endpoint URL, timestamp, and response code – but mask anything confidential.
When should I use a custom table for API logs?
Use a custom database table for API logs when you need structured data that’s easy to filter, search, and manage in the WordPress admin dashboard.
This setup works best when you’re dealing with a lot of log entries, more complex queries, or a familiar admin view built with WP_List_Table. It gives you a clean way to sort through data without digging through messy files.
That said, custom tables can put more load on the database. So in some cases, developers split the job: files store raw logs, while the database keeps only the errors that need action.

