APIs let WordPress talk to other services: weather, payments, CRMs, maps, stock data. Done well, your site shows live data without hand-editing pages. Done badly, you leak API keys, hammer third-party quotas, or ship a custom REST route that exposes admin data to strangers.
This guide covers outbound integrations (WordPress calls Stripe) and inbound REST security (other apps call your WordPress). Both matter for a typical agency build.

Two APIs, two directions
The WordPress REST API is how apps talk to WordPress. wp_remote_get() is how WordPress talks out. Many sites use both.
WordPress REST API security basics
Public read endpoints for posts are by design. Risk clusters in custom plugin routes:
- Authentication: Application Passwords, OAuth plugins, or cookie auth for logged-in users. Do not invent weak token schemes.
- Permission callbacks: Every
register_rest_route() needs a real permission_callback, not __return_true for sensitive data.
- Capabilities: Match
current_user_can() to the data returned. Subscriber-only data should not leak to anonymous callers.
- Rate limiting: Pair with firewall or host WAF when endpoints get hammered.
- Discovery:
/wp-json/ lists routes. Hiding the index is optional; fixing permissions is mandatory.
Audit custom plugins and headless front ends after major updates. A route that was “internal only” often becomes public when someone forgets the callback.
Calling an external API from PHP
Prefer the WordPress HTTP API over raw curl unless you have a specific need.
$response = wp_remote_get(
'https://api.example.com/v1/status',
array(
'timeout' => 15,
'headers' => array(
'Authorization' => 'Bearer ' . EXAMPLE_API_TOKEN,
),
)
);
if ( is_wp_error( $response ) ) {
return;
}
$code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
wp_remote_post() follows the same pattern for writes. Put reusable logic in a small custom plugin, not only in the theme’s functions.php, so the integration survives theme switches.
Walkthrough: shortcode that shows remote status
Goal: show a short status string from https://api.example.com/v1/status without exposing the API token to browsers.
- Store the secret in
wp-config.php (or the host env), not in the database options screen:
define( 'EXAMPLE_API_TOKEN', 'replace-me' );
- Fetch on the server, cache the result, and fail closed:
function example_get_remote_status() {
$cached = get_transient( 'example_api_status' );
if ( false !== $cached ) {
return $cached;
}
$response = wp_remote_get(
'https://api.example.com/v1/status',
array(
'timeout' => 15,
'headers' => array(
'Authorization' => 'Bearer ' . EXAMPLE_API_TOKEN,
),
)
);
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return '';
}
$data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! is_array( $data ) || empty( $data['status'] ) ) {
return '';
}
$status = sanitize_text_field( $data['status'] );
set_transient( 'example_api_status', $status, 5 * MINUTE_IN_SECONDS );
return $status;
}
- Print with escaping in a shortcode:
add_shortcode(
'example_status',
function () {
$status = example_get_remote_status();
if ( '' === $status ) {
return '<p>Status unavailable.</p>';
}
return '<p>Status: ' . esc_html( $status ) . '</p>';
}
);
-
Optional REST wrapper if JavaScript needs the value: register a custom route that calls the same helper and returns JSON. Still keep EXAMPLE_API_TOKEN only in PHP. Check capabilities if the data is not public.
-
Operate it: log failures, watch provider rate limits, rotate the token when people leave, and delete the plugin when the integration dies.
Application Passwords and automation
WordPress Application Passwords let scripts authenticate to REST without sharing the main admin password. Use them for:
- Staging sync tools
- Headless front ends
- Internal automation with scoped accounts
Give each integration its own user with the minimum role. Revoke the application password when the tool is removed. Pair with 2FA on human admin accounts. Guide: login security.
Plugins vs custom code
No-code API plugins (WPGetAPI-style tools) help non-developers map endpoints in wp-admin. Custom code gives you control, tests, and clearer security review.
Either way:
- Read the provider’s auth and rate-limit docs
- Store credentials outside the theme repo when you can
- Cache responses that do not need to be live every page view
- Validate and sanitize anything you print
Secure integration habits
Protect keys. Do not hardcode secrets in public repos or paste them into page content. wp-config.php constants, environment variables, or a secrets store beat committing tokens to Git.
Least privilege. Use scoped API keys. Revoke keys you no longer need. See least privilege.
Validate responses. Never trust remote JSON blindly. Check HTTP status codes. Escape output when rendering HTML (XSS lives in bad prints).
Rate limit and cache. Call APIs on cron or on demand with transients, not on every anonymous page hit, unless the provider and your host can take it.
Error handling. Failed calls should degrade gracefully, not white-screen the site.
Log without leaking. When debugging, do not log full API responses that contain PII or tokens.
Where WP Security Ninja fits
WP Security Ninja helps with site security posture: tests, vulnerability scanning, Cloud Firewall, malware tools, and login hardening. It is not an API connector builder and it does not “auto-integrate” Stripe or weather feeds for you.
Use Security Ninja to keep the WordPress install harder to abuse while your integration code (or a dedicated API plugin) handles the external calls. Broader baseline: WordPress security checklist, plugin risks.
Quick start checklist
- Pick the API and confirm auth, HTTPS endpoints, and quotas
- Audit custom REST routes for permission callbacks
- Decide plugin UI vs custom plugin code
- Store the key securely; never expose private tokens to the browser
- Fetch with
wp_remote_get / wp_remote_post, parse JSON, handle errors
- Cache, display safely, and monitor failures in logs
- Re-test after WordPress and plugin updates
APIs make WordPress more useful. Treat every key like a password, every REST route like a public door unless proven otherwise, and every remote payload like untrusted input.