Le 1er mars 2023, OpenAI ouvre au public l’API Chat Completions et le modèle gpt-3.5-turbo. Pour un développeur WordPress habitué aux appels HTTP classiques, la nouveauté tient moins à la technique qu’au format des messages échangés : une liste de rôles (system, user, assistant) plutôt qu’un simple bloc de texte à compléter.
Aucune bibliothèque officielle en PHP n’est fournie à cette date. La fonction wp_remote_post, déjà présente dans le cœur, suffit largement pour construire ce premier appel : elle gère les en-têtes, le corps JSON et les erreurs réseau sans dépendance supplémentaire.
Choisir le bon hook pour déclencher l’appel
Le choix du hook détermine si l’appel bloque ou non le rendu d’une page. Sur une page publique, un appel synchrone dans template_redirect ralentirait chaque visite. Il est préférable de le déclencher depuis un contexte administrateur, par exemple lors de l’enregistrement d’un article :
save_postpour lancer un traitement à la publicationadmin_post_{action}pour une action déclenchée par un bouton d’administration- Un événement planifié via
wp_schedule_single_eventpour différer l’appel
Construire la requête avec wp_remote_post
Le corps de la requête suit le format attendu par l’API : un tableau messages, un modèle, et éventuellement une température. La clé API se transmet dans l’en-tête Authorization.

add_action( 'save_post', function ( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) {
return;
}
$api_key = get_option( 'monplugin_openai_key' );
if ( empty( $api_key ) ) {
return;
}
$response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', array(
'timeout' => 20,
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( array(
'model' => 'gpt-3.5-turbo',
'messages' => array(
array( 'role' => 'system', 'content' => 'Tu résumes un article en deux phrases.' ),
array( 'role' => 'user', 'content' => get_post_field( 'post_content', $post_id ) ),
),
) ),
) );
} );
Lire et valider la réponse
La réponse arrive sous forme de texte JSON, qu’il faut décoder puis vérifier avant de l’utiliser. Deux échecs sont fréquents : un code HTTP différent de 200, ou une structure inattendue si l’API a renvoyé une erreur métier plutôt qu’un problème réseau.
if ( is_wp_error( $response ) ) {
error_log( 'Erreur réseau OpenAI : ' . $response->get_error_message() );
return;
}
$code = wp_remote_retrieve_response_code( $response );
$data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( 200 !== (int) $code || empty( $data['choices'][0]['message']['content'] ) ) {
error_log( 'Réponse OpenAI inattendue, code ' . $code );
return;
}
$resume = sanitize_textarea_field( $data['choices'][0]['message']['content'] );
update_post_meta( $post_id, '_resume_ia', $resume );
Gérer le délai d’attente
Le paramètre timeout mérite une attention particulière : la valeur par défaut de wp_remote_post est de cinq secondes, souvent trop courte pour une génération de texte. Vingt secondes constituent un compromis raisonnable pour un appel déclenché en tâche de fond plutôt qu’au chargement d’une page.
Sécuriser la clé API
Stocker la clé dans une option WordPress classique via update_option l’expose dans un export de base de données non chiffré. Une alternative simple consiste à la lire depuis une constante définie dans wp-config.php, en dehors de la racine web :
define( 'MONPLUGIN_OPENAI_KEY', 'sk-xxxxxxxxxxxxxxxx' );
Le plugin lit alors la constante si elle existe, avant de retomber sur l’option en base pour les environnements où elle n’est pas définie.
Un appel à une API externe ne doit jamais bloquer la publication d’un contenu : en cas d’échec, l’article se publie normalement, sans résumé généré, quitte à relancer le traitement plus tard.
Pour aller plus loin
Ce premier appel reste volontairement minimal : pas de gestion de file d’attente, pas de nouvelle tentative automatique en cas d’échec temporaire. Ces raffinements ont leur place une fois le principe de base validé sur un cas réel, avec un volume d’articles suffisant pour justifier l’investissement.
La logique reste la même quel que soit le fournisseur choisi par la suite : un hook déclencheur, une requête HTTP sortante, une réponse validée avant d’être enregistrée. C’est cette structure, plus que le fournisseur, qui vieillit bien.