Parse Resumes in PHP and Laravel
Two official Composer packages, one for parsing and one for match scoring, plus a raw Guzzle fallback for framework-free PHP. Working code for all three, nothing hypothetical.
To parse a resume in Laravel, install sharpapi/laravel-resume-parser, set SHARP_API_KEY in your .env, and call ResumeParserService::parseResume(). The service returns a status URL; fetchResults() polls it and hands you the structured candidate data.
Laravel — the parser package
use SharpAPI\ResumeParser\ResumeParserService;
class ImportResumeController extends Controller
{
public function __construct(private ResumeParserService $resumeParser) {}
public function store(Request $request)
{
$path = $request->file('resume')->getRealPath();
$statusUrl = $this->resumeParser->parseResume($path, 'English');
$result = $this->resumeParser->fetchResults($statusUrl);
return response()->json($result->getResultJson());
}
}
fetchResults() handles the polling loop internally — each job typically finishes in a few seconds. For upload-heavy apps, move the two calls into a queued job so the HTTP request returns instantly.
Laravel — match scoring, same pattern
The sibling package sharpapi/laravel-resume-job-match-score scores the parsed candidate against a job description — including the context directives that tune the weighting:
use SharpAPI\ResumeMatchScore\ResumeMatchScoreService;
$service = new ResumeMatchScoreService();
$context = "EMPHASIZE: backend scalability\n"
."DEEMPHASIZE: frontend frameworks\n"
."CREDIT: AWS";
$statusUrl = $service->matchResumeToJob(
storage_path('resumes/candidate.pdf'),
'We are hiring a Senior Backend Developer (PHP/Laravel)…',
'English',
$context,
);
$scores = $service->fetchResults($statusUrl)->toArray();
// $scores['match_scores']['overall_match'] → 0–100
Plain PHP — no framework, no package
The endpoint is ordinary multipart REST, so Guzzle alone covers it:
$client = new \GuzzleHttp\Client();
$submit = $client->post('https://sharpapi.com/api/v1/hr/parse_resume', [
'headers' => ['Authorization' => 'Bearer '.$apiKey],
'multipart' => [
['name' => 'file', 'contents' => fopen('candidate.pdf', 'r')],
['name' => 'language', 'contents' => 'English'],
],
]);
$statusUrl = json_decode((string) $submit->getBody(), true)['status_url'];
do {
sleep(2);
$job = json_decode((string) $client->get($statusUrl, [
'headers' => ['Authorization' => 'Bearer '.$apiKey],
])->getBody(), true);
} while ($job['data']['attributes']['status'] === 'pending');
$candidate = $job['data']['attributes']['result'];
Where to take it
The full response schema is explorable at resume to JSON; the field reference lists all 50+ fields for your migrations. Parsing a legacy database of CVs? The bulk parsing guide covers queues, webhooks and cost estimation for Laravel specifically.
Questions, answered
Is there an official Laravel package?
Yes — composer require sharpapi/laravel-resume-parser. Set SHARP_API_KEY in .env, inject ResumeParserService, and call parseResume($path, $language). A sibling package covers job match scoring.
Can I use plain PHP without Laravel?
Yes — the endpoint is standard multipart REST, so Guzzle or curl works directly. The Laravel package just removes the boilerplate: auth header, multipart encoding and the polling loop.
How should I parse thousands of resumes in Laravel?
Dispatch one queued job per file and let each submit to the API and register a webhook. The API side is async by design, so your queue concurrency is the only throttle. Details in the bulk parsing guide.