Como criar um endpoint REST customizado no WordPress (passo a passo)

Capa: Criando seu próprio endpoint REST no WordPress
Mais cedo ou mais tarde todo dev WordPress precisa expor um dado para fora do site: alimentar um app, um front headless em React, um painel interno ou uma integração com outro sistema. A boa notícia é que o WordPress já tem uma API REST embutida — e criar a sua rota é mais simples do que parece. Neste passo a passo você vai criar um endpoint do zero, com filtros, sanitização, controle de acesso e uma rota com parâmetro na URL. No fim, é só chamar /wp-json/meusite/v1/produtos e receber JSON.

O que você vai precisar

    • Um WordPress rodando (local ou em produção).
    • Acesso ao functions.php do tema ou a um plugin próprio.
    • Noções básicas de PHP. Nada além disso.

Passo 1 — Onde colocar o código

Você pode escrever no functions.php do tema, mas a melhor prática é criar um plugin pequeno — assim a sua API continua funcionando mesmo se você trocar de tema. Crie o arquivo wp-content/plugins/minha-api/minha-api.php com um cabeçalho de plugin e ative-o no painel:
				
					<?php
/*
 * Plugin Name: Minha API
 * Description: Endpoints REST customizados do site.
 * Version: 1.0.0
 */
if (!defined('ABSPATH')) exit;
				
			

Passo 2 — Registrar a rota

Toda rota é registrada no hook rest_api_init com a função register_rest_route(). Repare em duas coisas: o namespace versionado (meusite/v1) — versionar evita quebrar quem já consome a API — e o permission_callback, que é obrigatório.

				
					add_action('rest_api_init', function () {
    register_rest_route('meusite/v1', '/produtos', array(
        'methods'             =&gt; 'GET',
        'callback'            =&gt; 'meusite_listar_produtos',
        'permission_callback' =&gt; '__return_true', // pública por enquanto
    ));
});
				
			

Passo 3 — O callback que devolve os dados

O callback recebe a requisição e devolve um WP_REST_Response. Aqui usei dados fixos para o exemplo, mas é onde você faria sua consulta com WP_Query, $wpdb ou o que precisar.

				
					function meusite_listar_produtos($request) {
    $produtos = array(
        array('id' =&gt; 1, 'nome' =&gt; 'Camiseta', 'preco' =&gt; 79.9),
        array('id' =&gt; 2, 'nome' =&gt; 'Caneca',   'preco' =&gt; 39.9),
    );

    return new WP_REST_Response($produtos, 200);
}
				
			

Pronto: já dá para abrir /wp-json/meusite/v1/produtos no navegador e ver o JSON.

Passo 4 — Receber e sanitizar parâmetros

Filtros chegam pela query string (?q=...&per_page=...). Nunca confie no que vem do usuário: sanitize tudo e limite os valores. É a diferença entre uma API segura e um convite a problemas.

				
					function meusite_listar_produtos($request) {
    $busca = sanitize_text_field($request-&gt;get_param('q'));

    $por_pagina = (int) $request-&gt;get_param('per_page');
    if ($por_pagina &lt; 1)   $por_pagina = 10;
    if ($por_pagina &gt; 100) $por_pagina = 100;

    // ... use $busca e $por_pagina na sua consulta ...

    return new WP_REST_Response(array(
        'busca'      =&gt; $busca,
        'por_pagina' =&gt; $por_pagina,
        'itens'      =&gt; array(),
    ), 200);
}
				
			

Passo 5 — Controlar o acesso

Use '__return_true' só quando o dado for realmente público (um catálogo, por exemplo). Para qualquer coisa sensível, valide a permissão no permission_callback:

				
					'permission_callback' =&gt; function () {
    return current_user_can('edit_posts');
},
				
			

Passo 6 — Uma rota com parâmetro na URL

Para buscar um item específico (ex.: /produtos/1), declare o parâmetro com uma expressão regular no caminho:

				
					register_rest_route('meusite/v1', '/produtos/(?P&lt;id&gt;\d+)', array(
    'methods'             =&gt; 'GET',
    'callback'            =&gt; 'meusite_obter_produto',
    'permission_callback' =&gt; '__return_true',
));

function meusite_obter_produto($request) {
    $id = (int) $request['id'];

    if ($id !== 1) {
        return new WP_Error('nao_encontrado', 'Produto não encontrado.', array('status' =&gt; 404));
    }

    return new WP_REST_Response(array('id' =&gt; 1, 'nome' =&gt; 'Camiseta'), 200);
}
				
			

Passo 7 — Testar

No navegador, abra a URL. No terminal, use curl para ver também o status e os headers:

				
					curl "https://seusite.com.br/wp-json/meusite/v1/produtos?per_page=5"
curl "https://seusite.com.br/wp-json/meusite/v1/produtos/1"
				
			

Se o seu site não usa permalinks "bonitos", a forma alternativa é ?rest_route=/meusite/v1/produtos.

Boas práticas para levar para produção

  • Versione o namespace (v1, v2): você muda a API sem quebrar quem já consome.
  • Sanitize a entrada, controle a saída: nunca exponha campos sensíveis (e-mail, telefone, dados internos) sem necessidade.
  • Pagine listas grandes (page/per_page) e devolva o total nos headers.
  • Pense no consumidor: se um front em outro domínio vai chamar a API, configure o CORS.
  • Cacheie respostas pesadas para não sobrecarregar o banco a cada requisição.

Conclusão

Com register_rest_route(), um callback e um pouco de cuidado com segurança, o seu WordPress vira um back-end pronto para alimentar apps, fronts headless e integrações. É o mesmo padrão que usamos em projetos reais aqui na InfotechJS para servir catálogos e conectar sistemas.

Leia também: como travar uma rota REST aberta, com rate-limit por IP, honeypot e checagem de origem, e o que muda quando essa rota passa a receber webhook de loja.

Quer transformar o seu WordPress em um CMS headless ou integrá-lo a outro sistema? Fale com a gente.

Do mesmo assunto

Tutoriais, WordPress

25 ago 2026

Webhook de pedido no WordPress: não perder e não duplicar

Webhook de loja chega duas vezes, chega fora de ordem
Capa: Tema filho, a armadilha do index.php

Tutoriais, WordPress

20 ago 2026

Tema filho no WordPress: por que o index.php pode deixar sua página em branco

Um tema filho com um index.php genérico pode devolver 200
Como sanitizar corretamente HTML vindo do ACF no WordPress

Desenvolvimento WordPress

22 abr 2025

Sanitização no WordPress: protegendo HTML do ACF e wp_kses

A sanitização no WordPress: protegendo HTML do ACF com wp_kses

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *