alex-volga / assemblyai-php
Modern PHP SDK for AssemblyAI Speech-to-Text and Audio Intelligence API
Requires
- php: >=8.3
- guzzlehttp/guzzle: ^7.8
- psr/http-client: ^1.0
- psr/http-factory: ^1.0 || ^1.1
- psr/http-message: ^1.0 || ^2.0
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A modern, strongly typed PHP SDK for the AssemblyAI Speech-to-Text and Audio Intelligence API.
Designed from the ground up following modern PHP 8.3+ best practices:
- PHP 8.3+:
readonlyclasses and DTOs, typed class constants, Backed Enums, pattern matching (match), and strict typing (declare(strict_types=1)). - PSR Standards Compliant: PSR-7 (HTTP Messages), PSR-17 (HTTP Factories), and PSR-18 (HTTP Client). Decoupled from HTTP implementations — works out-of-the-box with Guzzle, or inject any PSR-18 client (e.g. Symfony HTTP Client).
- Full API Coverage:
- Asynchronous Speech-to-Text transcription with support for 100+ languages.
- Media uploading for local files and resource streams.
- Speaker Diarization (speaker detection and labeling).
- Audio Intelligence (Auto Chapters, Sentiment Analysis, Named Entity Detection, Summarization, Content Safety).
- PII Redaction and Redacted Audio generation.
- Subtitles export in SRT and VTT formats.
- LeMUR (Large Language Models applied to speech: custom prompts, Q&A, summaries, and action item extraction).
- Temporary token generation for secure client-side Real-Time WebSockets streaming.
Installation
Install via Composer:
composer require alex-volga/assemblyai-php
Requirements:
- PHP 8.3 or higher
ext-jsonandext-curl(or an alternative PSR-18 HTTP client)
Quickstart
Transcribe a local file or remote URL in one step
The high-level transcribe() method automatically uploads a local file or stream, dispatches the transcription job, polls until completion, and returns a typed Transcript object:
use AssemblyAI\AssemblyAI; $client = new AssemblyAI('YOUR_ASSEMBLYAI_API_KEY'); // Pass a local file path, open stream resource, or public audio URL: $transcript = $client->transcribe('/path/to/podcast.mp3'); echo "Status: " . $transcript->status->value . PHP_EOL; echo "Text: " . $transcript->text . PHP_EOL; echo "Duration: " . $transcript->audioDuration . " seconds" . PHP_EOL;
Client Configuration
Initialization
use AssemblyAI\AssemblyAI; use AssemblyAI\Config; // Basic initialization: $client = new AssemblyAI('YOUR_API_KEY'); // Or configure using the Config object: $config = new Config( apiKey: 'YOUR_API_KEY', baseUrl: Config::DEFAULT_BASE_URL, // or Config::EU_BASE_URL timeout: 60.0, // HTTP request timeout in seconds pollInterval: 3, // Polling check interval in seconds pollTimeout: 600, // Maximum time to wait for completion in seconds additionalHeaders: ['X-Custom-Header' => 'value'] ); $client = new AssemblyAI($config); // Factory method for EU Data Residency: $clientEu = AssemblyAI::createEu('YOUR_API_KEY');
Using a Custom PSR-18 HTTP Client
The SDK is not locked into a single HTTP client. Inject any Psr\Http\Client\ClientInterface implementation:
use AssemblyAI\AssemblyAI; use Symfony\Component\HttpClient\Psr18Client; $psrClient = new Psr18Client(); $client = new AssemblyAI(config: 'YOUR_API_KEY', httpClient: $psrClient);
Transcripts API
Uploading Media Files
If your media file is stored locally, upload it to AssemblyAI's servers to obtain an upload_url:
// Upload via local file path: $uploadUrl = $client->files()->upload('/path/to/meeting.mp3'); // Or upload a stream resource: $stream = fopen('php://temp', 'r+'); // ... $uploadUrl = $client->files()->upload($stream);
Creating Transcripts with Advanced Options
Use the TranscriptParams builder to configure transcription options:
use AssemblyAI\Enums\SpeechModel; use AssemblyAI\Enums\SummaryModel; use AssemblyAI\Enums\SummaryType; use AssemblyAI\Enums\PiiPolicy; use AssemblyAI\Enums\PiiSubstitution; use AssemblyAI\Parameters\TranscriptParams; $params = TranscriptParams::create($uploadUrl) // Speaker Diarization ->withSpeakerLabels(enabled: true, speakersExpected: 2) // Speech Model and Language ->withSpeechModel(SpeechModel::Universal2) ->withLanguageCode('en_us') // or ->withLanguageDetection(true) // Formatting & Punctuation ->withPunctuate(true) ->withFormatText(true) // Audio Intelligence ->withAutoChapters(true) ->withSentimentAnalysis(true) ->withEntityDetection(true) ->withSummarization(SummaryType::Bullets, SummaryModel::Informative) // PII Redaction ->withPiiRedaction( policies: [PiiPolicy::EmailAddress, PiiPolicy::PhoneNumber, PiiPolicy::CreditCardNumber], sub: PiiSubstitution::EntityType ) // Contextual Prompting & Word Boosting ->withPrompt('Technical podcast discussing machine learning and software architecture') ->withWordBoost(['AssemblyAI', 'PHP', 'SDK'], 'high'); $created = $client->transcripts()->create($params); echo "Job ID: " . $created->id; // Status: queued
Polling for Completion
$transcript = $client->transcripts()->waitUntilReady( id: $created->id, pollInterval: 2, // poll every 2 seconds timeout: 300, // timeout after 5 minutes onPoll: function ($current) { echo "Current status: {$current->status->value}\n"; } ); if ($transcript->isCompleted()) { echo "Transcript Text: " . $transcript->text; }
Inspecting Transcript Results
The Transcript object exposes typed Value Objects:
// Words with timestamps and confidence scores: foreach ($transcript->words as $word) { echo "{$word->start}ms - {$word->end}ms: {$word->text} (confidence: {$word->confidence})\n"; } // Speaker turns (Utterances): foreach ($transcript->utterances as $utterance) { echo "Speaker {$utterance->speaker}: {$utterance->text}\n"; } // Auto Chapters: foreach ($transcript->chapters as $chapter) { echo "Chapter: {$chapter->headline}\n"; echo "Summary: {$chapter->summary}\n"; echo "Time: {$chapter->start}ms - {$chapter->end}ms\n\n"; } // Sentiment Analysis: foreach ($transcript->sentimentAnalysisResults as $sentiment) { echo "[{$sentiment->sentiment}] {$sentiment->text}\n"; } // Named Entity Detection: foreach ($transcript->entities as $entity) { echo "Entity: {$entity->text} (type: {$entity->entityType})\n"; }
Sentences and Paragraphs
// Sentences with individual timestamps: $sentencesResponse = $client->transcripts()->sentences($transcriptId); foreach ($sentencesResponse->sentences as $sentence) { echo $sentence->text . PHP_EOL; } // Paragraphs: $paragraphsResponse = $client->transcripts()->paragraphs($transcriptId); foreach ($paragraphsResponse->paragraphs as $paragraph) { echo $paragraph->text . PHP_EOL; }
Exporting Subtitles (SRT / VTT)
use AssemblyAI\Enums\SubtitleFormat; // Export in SRT format: $srt = $client->transcripts()->subtitles( id: $transcriptId, format: SubtitleFormat::Srt, charsPerCaption: 32 // optional maximum characters per caption line ); file_put_contents('subtitles.srt', $srt); // Export in WebVTT format: $vtt = $client->transcripts()->subtitles($transcriptId, SubtitleFormat::Vtt); file_put_contents('subtitles.vtt', $vtt);
Word Search in Transcripts
$search = $client->transcripts()->wordSearch($transcriptId, ['artificial', 'intelligence']); echo "Total occurrences found: " . $search->totalCount;
Listing and Deleting Transcripts
use AssemblyAI\Parameters\TranscriptListParams; use AssemblyAI\Enums\TranscriptStatus; // List transcripts with optional filters: $list = $client->transcripts()->list(new TranscriptListParams( limit: 10, status: TranscriptStatus::Completed )); foreach ($list->items as $item) { echo "ID: {$item->id}, Status: {$item->status->value}, Created: {$item->created}\n"; } // Delete a transcript: $client->transcripts()->delete($transcriptId);
LeMUR (LLMs on Speech)
Apply Large Language Models (such as Claude 3.5 Sonnet) directly across one or more transcripts.
Custom Prompt (Task)
use AssemblyAI\Enums\LemurModel; use AssemblyAI\Parameters\LemurTaskParams; $response = $client->lemur()->task(new LemurTaskParams( transcriptIds: [$transcriptId], prompt: 'Provide a concise summary of the key meeting takeaways and decisions made.', finalModel: LemurModel::Claude35Sonnet )); echo $response->response; echo "Input tokens used: " . $response->usage?->inputTokens;
Question & Answer (Q&A)
use AssemblyAI\Parameters\LemurQnAParams; $response = $client->lemur()->questionAnswer(new LemurQnAParams( transcriptIds: [$transcriptId], questions: [ ['question' => 'What deadlines were agreed upon?'], ['question' => 'Who is responsible for QA testing?'], ] )); echo $response->response;
Action Items Extraction
use AssemblyAI\Parameters\LemurActionItemsParams; $response = $client->lemur()->actionItems(new LemurActionItemsParams( transcriptIds: [$transcriptId] )); echo $response->response;
Purging LeMUR Response Data
$client->lemur()->purgeData($response->requestId);
Real-Time Streaming (Temporary WebSocket Tokens)
To connect browser or mobile client applications to the AssemblyAI Real-Time WebSocket transcription service securely without exposing your permanent API key, generate a single-use temporary token:
// Generate a token valid for opening a session within 60 seconds: $token = $client->realtime()->createTemporaryToken( expiresInSeconds: 60, maxSessionDurationSeconds: 3600 // max session length of 1 hour ); // Pass this token to your client application: // wss://streaming.assemblyai.com/v3/ws?token={token}
Error Handling
The SDK provides a clean, typed exception hierarchy rooted in AssemblyAI\Exceptions\AssemblyAIException:
AssemblyAIException(base interface)ClientException(base runtime exception)AuthenticationException— authentication error (401/403 invalid API key)NotFoundException— resource or transcript not found (404)ValidationException— invalid request parameters (400/422)RateLimitException— rate limit exceeded (429)ServerException— AssemblyAI internal server error (5xx)TranscriptionFailedException— transcript failed processing with statuserrorTimeoutException— polling exceeded timeout limit inwaitUntilReady
use AssemblyAI\Exceptions\AuthenticationException; use AssemblyAI\Exceptions\TranscriptionFailedException; use AssemblyAI\Exceptions\TimeoutException; use AssemblyAI\Exceptions\AssemblyAIException; try { $transcript = $client->transcribe('/path/to/audio.mp3'); } catch (AuthenticationException $e) { echo "Authentication failed: " . $e->getMessage(); } catch (TranscriptionFailedException $e) { echo "Transcription failed: " . $e->getMessage(); echo "Transcript ID: " . $e->getTranscriptId(); } catch (TimeoutException $e) { echo "Polling timed out: " . $e->getMessage(); } catch (AssemblyAIException $e) { echo "SDK error: " . $e->getMessage(); }
Testing & Static Analysis
# Run Unit and Feature tests with PHPUnit vendor/bin/phpunit # Run static analysis with PHPStan (Level 8) vendor/bin/phpstan analyse
License
MIT License. See LICENSE for details.