Stated as directly as possible: the WordPress PHP AI Client SDK does not offer audio
transcription today. There is no transcribe(), no generate_transcript_result(), no
method of any name for turning an audio file into text anywhere in the SDK’s public
API. Audio appears in the SDK as an output modality, generating speech from text is a
real, confirmed capability, and as an input modality for models that can reason about
audio content in a broader sense. But speech-to-text transcription specifically isn’t
exposed as a capability the SDK’s CapabilityEnum or builder methods currently name.
If a project needs transcription today, the real path is calling a provider’s
transcription API directly over HTTP, outside wp_ai_client_prompt() entirely, using
that provider’s own API key and endpoint. This lesson shows that real path.
Course 5’s lesson on managing API keys across providers gives useful background on credential storage, even though this lesson’s credential is for a service outside the WP AI Client’s own provider system entirely.
Nothing in this lesson calls wp_ai_client_prompt() or any other WP AI Client SDK
method. That’s deliberate: transcription isn’t something the SDK does yet, so the
honest answer is a plain HTTP call to a transcription provider’s own API, managed
entirely outside the SDK’s provider abstraction. If the SDK adds transcription support
later, that would be the better path going forward, check its repository before
assuming this lesson’s approach is still the recommended one.
Step 1: Why you can’t reach for wp_ai_client_prompt() here
Every builder method covered so far, generate_text(), AiClient::input()->generateEmbedding(),
generate_image_result(), maps to a capability documented in the SDK’s own
CapabilityEnum: text generation, image generation, embedding generation, speech
generation, text-to-speech conversion, video generation, music generation. Speech-to-text
transcription isn’t one of the listed capabilities. Calling generate_text() with an
audio file attached might work for some broader “listen to this and answer a question
about it” use case on a model that supports audio input, but that’s a different job
from producing an accurate, complete, word-for-word transcript, and it isn’t a
confirmed, purpose-built path either. For an actual transcript, go directly to a
provider’s transcription API.
Step 2: Store the provider credential separately
Because this call bypasses the WP AI Client SDK’s provider configuration entirely, it needs its own credential, stored separately from whatever keys Course 5 had you put into the AI Client’s own settings:
// File: my-plugin/class-audio-transcription.php
function my_plugin_get_transcription_api_key(): string {
$key = defined( 'MY_PLUGIN_TRANSCRIPTION_API_KEY' ) ? MY_PLUGIN_TRANSCRIPTION_API_KEY : '';
if ( ! $key ) {
$key = get_option( 'my_plugin_transcription_api_key', '' );
}
return $key;
}
Same pattern Course 5 recommended for AI Client provider keys, a constant in
wp-config.php first, an option as a fallback for a settings-screen-based setup. The
point is that this key belongs to a transcription provider’s account, not to whichever
provider you configured for text and image generation, they may or may not be the same
company, and even when they are, the API keys are typically not interchangeable between
their different product APIs.
Step 3: Sending the audio file over HTTP
Transcription endpoints generally expect the audio uploaded as multipart form data,
not embedded as base64 inside a JSON body the way File objects work elsewhere in this
course. WordPress’s HTTP API supports multipart bodies directly:
// File: my-plugin/class-audio-transcription.php
function my_plugin_transcribe_audio( string $file_path ): string|WP_Error {
$api_key = my_plugin_get_transcription_api_key();
if ( ! $api_key ) {
return new WP_Error( 'missing_api_key', 'No transcription API key configured.' );
}
$boundary = wp_generate_password( 24, false );
$file_data = file_get_contents( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
if ( false === $file_data ) {
return new WP_Error( 'unreadable_file', 'Could not read the audio file.' );
}
$body = "--{$boundary}\r\n";
$body .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file_path ) . "\"\r\n";
$body .= "Content-Type: audio/mpeg\r\n\r\n";
$body .= $file_data . "\r\n";
$body .= "--{$boundary}\r\n";
$body .= "Content-Disposition: form-data; name=\"model\"\r\n\r\n";
$body .= "your-provider-transcription-model\r\n";
$body .= "--{$boundary}--\r\n";
$response = wp_remote_post(
'https://api.your-transcription-provider.example/v1/audio/transcriptions',
array(
'timeout' => 120,
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'multipart/form-data; boundary=' . $boundary,
),
'body' => $body,
)
);
if ( is_wp_error( $response ) ) {
return $response;
}
$code = wp_remote_retrieve_response_code( $response );
$data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( 200 !== $code || ! is_array( $data ) || empty( $data['text'] ) ) {
return new WP_Error( 'transcription_failed', 'Transcription request did not return usable text.' );
}
return $data['text'];
}
The exact endpoint path, required form fields, and response shape differ by transcription provider, this code shows the general multipart pattern, not a specific provider’s documented contract. Before shipping this, check your chosen provider’s own current API reference for the real endpoint, required parameters, accepted audio formats, and file size limits, and adjust accordingly.
Step 4: A longer timeout, deliberately
Notice the 'timeout' => 120 above. Transcription of anything beyond a short clip
takes meaningfully longer than a typical text generation call, WordPress’s HTTP API
default timeout is far too short for a multi-minute audio file. Raise it explicitly
rather than letting a real transcription job time out partway through.
Step 5: Storing the transcript
// File: my-plugin/class-audio-transcription.php
$transcript = my_plugin_transcribe_audio( $file_path );
if ( ! is_wp_error( $transcript ) ) {
update_post_meta( $attachment_id, '_my_plugin_transcript', wp_kses_post( $transcript ) );
}
Storing the transcript as post meta on the audio attachment keeps it attached to the
file it belongs to, and sets up the next lesson, which summarizes a transcript like
this one with generate_text(), a step that’s back on solid, confirmed SDK ground.
Test it: transcribe a short real audio file
wp-env run cli wp eval '
$result = my_plugin_transcribe_audio( "/path/to/a/short-test-clip.mp3" );
if ( is_wp_error( $result ) ) {
echo "Error: " . $result->get_error_message() . "\n";
} else {
echo "Transcript: " . $result . "\n";
}
'
You should see either real transcribed text matching what’s actually said in the clip,
or a clear WP_Error message, not a silent failure.
It’s easy to reuse the same API key you configured for wp_ai_client_prompt() here and
have it fail confusingly, or worse, silently hit a different product’s usage limits on
the same account. Keep the transcription credential explicitly separate in your
settings UI and your code, with its own label, so nobody configuring this plugin later
assumes one key covers both.
Recap
There is no transcription method in the WordPress PHP AI Client SDK today, that’s a real
gap, not an oversight in this course. The honest, working path is a direct HTTP call to
a transcription provider’s own API using wp_remote_post(), multipart form data for
the audio file, that provider’s own API key stored separately from your AI Client
provider configuration, and a raised timeout to accommodate longer clips. Store the
resulting transcript as post meta, since the next lesson picks it up from there.
Resources & further reading
- PHP AI Client SDK repository, GitHub
- wp_remote_post() function reference, developer.wordpress.org
- Introducing the AI Client in WordPress 7.0, make.wordpress.org