Floreal API Documentation
What is Floreal?
Floreal is the AI layer for the HR industry—transforming how companies find, evaluate, and match candidates. We combine semantic search, intelligent automation, and conversational AI to streamline the entire recruitment workflow.
🚀 Our Products
1. Instant Match (Rolling out November 2025)
Real-time candidate matching powered by AI. Upload a job description and instantly get ranked candidates from your database with relevance scores, match explanations, and skills gap analysis. Perfect for high-volume recruiting and urgent hiring needs.
2. Search API
Build custom talent search into your applications. Our semantic search understands natural language queries and returns candidates based on meaning, not just keywords. Choose from Dense (semantic), Sparse (keyword), Hybrid (best quality), or Builtin-Rerank algorithms depending on your speed vs. accuracy needs.
3. HR Note Taker (Rolling out November 2025)
AI meeting assistant that joins your interviews, captures key insights, and automatically generates structured candidate evaluations. Never miss important details and eliminate manual note-taking during conversations.
4. Semantic CV Database
Automatically extract, structure, and index CVs for instant searchability. Our AI processes PDFs, DOCs, and text files to extract 100+ attributes including contact info, skills, experience, education, and domain expertise. Every CV becomes fully searchable through natural language. Includes automatic generation of skills competency matrices (fiches de compétences) showing technical abilities, domain expertise, and proficiency levels for standardized talent assessment.
5. Skills Competency Matrix
Generate comprehensive skill assessments and competency profiles for every candidate. Our AI analyzes CVs to create standardized skill matrices showing technical abilities, domain expertise, and proficiency levels—perfect for talent mapping and skills gap analysis.
Quick Links
Search
Document Upload
Search Groups
Authentication
All endpoints require an API key:
Authorization: x-API-key YOUR_API_KEYGet your API key at: https://www.floreal.ai/settings/api-keys
Base URL
https://api.floreal.aititle: "API Quickstart" description: "Get started with CV upload, management, and candidate search in minutes"
API Quickstart
Build your talent search platform in minutes. Upload CVs, let AI extract structured data, and search for candidates using semantic and keyword matching.
What You'll Build
📤 Upload CVs
Upload PDF/DOC CVs from URLs, files, or cloud storage
🤖 AI Processing
Automatic extraction of contact, skills, and experience
🔍 Semantic Search
Find candidates using natural language queries
📊 Structured Data
100+ attributes ready for filtering and analysis
Prerequisites
Before starting, you'll need:
- ✅ API Key - Get yours from dashboard settings
- ✅ Base URL -
https:/www.floreal.ai - ✅ HTTP Client - cURL, Postman, or any programming language
Complete Workflow in 3 Steps
Step 1: Upload a CV
Choose your upload method:
Easiest method - just provide a URL:
curl -X POST "https://api.floreal.aiv1/public/documents/upload-from-url" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/john-doe-cv.pdf",
"documentName": "John Doe - Software Engineer",
"documentDate": "11-2025"
}'Response:
{
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"status": "uploading",
"message": "Document fetched from URL and processing started",
"estimatedProcessingTime": "30-60 seconds"
}Simple single-step - upload file bytes directly:
curl -X POST "https://api.floreal.aiv1/public/documents/upload-direct?documentName=John%20Doe&documentDate=11-2025" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/pdf" \
--data-binary @resume.pdfFor large files - 3 steps, direct to S3:
# Step 1: Get presigned URL
curl -X POST "https://api.floreal.aiv1/public/documents/upload-presigned" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"fileName":"resume.pdf","contentType":"application/pdf","fileSize":245760}'
# Step 2: Upload to S3
curl -X PUT "PRESIGNED_URL" \
-H "Content-Type: application/pdf" \
--data-binary @resume.pdf
# Step 3: Finalize
curl -X POST "https://api.floreal.aiv1/public/documents/upload-presigned-finalize" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"uploadId":"...","documentName":"John Doe","documentDate":"11-2025"}'Processing time: 30 to 90 seconds
Step 2: Wait for Processing
Poll the status endpoint until status === "completed":
async function waitForCompletion(documentId) {
for (let i = 0; i < 18; i++) {
// 90 seconds max
const res = await fetch(
`https://api.floreal.aiv1/public/documents/${documentId}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
if (data.status === "completed") {
console.log("✅ Processing complete!");
return data;
}
if (data.status === "failed") {
throw new Error(data.error?.message);
}
console.log(`⏳ Status: ${data.status}`);
await new Promise((r) => setTimeout(r, 5000)); // Wait 5s
}
throw new Error("Timeout");
}
const result = await waitForCompletion("789e4567-...");
console.log("Contact:", result.contact);
console.log("Profile:", result.profile);import time
import requests
def wait_for_completion(document_id):
for i in range(18): # 90 seconds max
response = requests.get(
f'https://api.floreal.aiv1/public/documents/{document_id}',
headers={'X-API-Key': 'YOUR_API_KEY'}
)
data = response.json()
if data['status'] == 'completed':
print('✅ Processing complete!')
return data
if data['status'] == 'failed':
raise Exception(data.get('error', {}).get('message'))
print(f"⏳ Status: {data['status']}")
time.sleep(5)
raise Exception('Timeout')
result = wait_for_completion('789e4567-...')
print('Contact:', result['contact'])
print('Profile:', result['profile'])# Poll manually every 5 seconds
curl "https://api.floreal.aiv1/public/documents/789e4567-..." \
-H "X-API-Key: YOUR_API_KEY"Step 3: Get Complete CV Data
Once status === "completed", the response includes:
{
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"status": "completed",
"summary": "Benjamin Gabay - Sales-marketing Professional with 4 years of experience...",
"extractedText": "Contact +33766771226\nLinkedIn: linkedin.com/in/benjamin-gabay\n\nBenjamin Gabay...",
"contact": {
"firstName": "Benjamin",
"lastName": "Gabay",
"email": "benjamin.gabay@example.com",
"linkedin": "www.linkedin.com/in/benjamin-gabay",
"location": {
"city": "Paris",
"country": "FRA"
}
},
"profile": {
"domain": "Sales Marketing",
"specialization": "Content Marketing",
"seniorityLevel": "Junior",
"experienceYears": 4,
"technicalStack": ["Copywriting", "Content Creation", "Prospecting"],
"industries": ["Real-estate", "Social Media"],
"recentExpertise": ["Ghostwriting", "LinkedIn Coaching"],
"hasManagement": false
},
"attributes": {
"languages": {
"french": 1,
"languages_count": 1
},
"technical_skills": {
"copywriting": 1,
"content_creation": 1,
"technical_skills_count": 3
},
"professional_experience": {
"total_experience_years": 4,
"number_of_employers": 3,
"average_tenure_months": 16
},
"education_indicators": {
"highest_degree": "Bachelor",
"education_field": ["Philosophy", "Literature"],
"first_school_name": "University of Paris I: Panthéon-Sorbonne"
}
}
}What you get:
- ✅ summary - AI-generated 2-3 sentence overview
- ✅ extractedText - Complete CV text (5-15KB) for custom analysis
- ✅ contact - Name, email, LinkedIn, location
- ✅ profile - Domain, seniority, experience, skills, industries
- ✅ attributes - 100+ structured fields for advanced filtering
Understanding the Data Structure
Summary
AI-generated professional overview including:
- Name and location
- Years of experience
- Educational background
- Key technical skills
- Domain expertise
- Recent specializations
Example:
"Benjamin Gabay - Sales-marketing Professional based in Paris, FRA with 4 years of experience. Educational background: Bachelor in Philosophy from University of Paris I. Technical expertise: Copywriting, Content Creation. Domain experience: Ghostwriting, LinkedIn Coaching, Lead Generation."
Extracted Text
Complete CV content (typically 5-15KB):
- Full unredacted text from the original CV
- Use for: full-text search, LLM analysis, custom parsing, document display
- Includes all original formatting and details
Contact Information
{
"firstName": "Benjamin",
"lastName": "Gabay",
"email": "benjamin.gabay@example.com", // or "Unknown" if not found
"linkedin": "www.linkedin.com/in/benjamin-gabay",
"location": {
"city": "Paris",
"country": "FRA" // ISO 3166-1 alpha-3 code
}
}Professional Profile
{
"domain": "Sales Marketing", // Primary professional area
"specialization": "Content Marketing", // Specific expertise
"seniorityLevel": "Junior", // Junior | Senior | Executive
"experienceYears": 4, // Total years of experience
"technicalStack": ["Copywriting", "Content Creation"],
"industries": ["Real-estate", "Social Media"],
"recentExpertise": ["Ghostwriting", "LinkedIn Coaching"],
"hasManagement": false // Leadership experience
}Attributes (100+ Fields)
Languages
{
"french": 1,
"english": 1,
"english_fluent": 1,
"languages_count": 2,
"cv_language_detected": "French"
}Technical Skills
{
"copywriting": 1,
"python_experience": 1,
"javascript_experience": 1,
"technical_skills_count": 3,
"programming_languages_count": 2
}Professional Experience
{
"total_experience_years": 4,
"number_of_employers": 3,
"average_tenure_months": 16,
"current_position_duration": 4,
"management_experience": 1,
"job_stability_score": 3,
"career_progression_score": 5
}Education
{
"highest_degree": "Bachelor",
"education_field": ["Philosophy", "Literature"],
"first_school_name": "University of Paris I: Panthéon-Sorbonne",
"multiple_degrees": 1,
"education_prestige_score": 3
}Industry Verticals
{
"real_estate": 1,
"social_media": 1,
"fintech": 1,
"verticals_count": 3
}Business Specialization
{
"roi_analysis": 1,
"training_experience": 1,
"revenue_generation_experience": 1,
"client_relationship_experience": 1,
"business_orientation": 3
}Market & Client Experience
{
"emea": 1,
"startup": 1,
"b2b_focus": 1,
"enterprise": 1,
"market_segments_count": 4
}Searching for Candidates
Once CVs are uploaded and processed, search using natural language or keywords:
Dense Search (Semantic)
Best for: Natural language queries, conceptual matching
curl -X POST "https://api.floreal.aiv1/public/searches/dense" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "experienced backend engineer who can lead teams",
"top_k": 20
}'Response:
{
"searchId": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"searchType": "dense",
"estimatedTime": "5-10 seconds",
"statusUrl": "/v1/public/searches/550e8400-...",
"resultsUrl": "/v1/public/searches/550e8400-.../results"
}Sparse Search (Keywords)
Best for: Exact skill matching, technical terms
curl -X POST "https://api.floreal.aiv1/public/searches/sparse" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Python, FastAPI, PostgreSQL, Docker, AWS",
"top_k": 20
}'Hybrid Search (Best Quality)
Best for: Complex queries, maximum accuracy
curl -X POST "https://api.floreal.aiv1/public/searches/hybrid" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "senior Python developer with startup experience who can mentor juniors",
"top_k": 30,
"rerank_top_n": 20
}'Processing time: 15-30 seconds
Getting Search Results
Step 1: Poll for Completion
curl "https://api.floreal.aiv1/public/searches/550e8400-..." \
-H "X-API-Key: YOUR_API_KEY"Wait for:
{
"searchId": "550e8400-...",
"status": "completed", // ← Ready!
"resultsCount": 42,
"resultsUrl": "/v1/public/searches/550e8400-.../results"
}Step 2: Fetch Results
curl "https://api.floreal.aiv1/public/searches/550e8400-.../results" \
-H "X-API-Key: YOUR_API_KEY"Response structure:
{
"searchId": "550e8400-...",
"query": "experienced backend engineer",
"searchType": "dense",
"results": [
{
"documentId": "789e4567-...",
"documentName": "Benjamin_Gabay_CV.pdf",
"score": 0.856,
"extractedText": "Complete CV text here...",
"summary": "Benjamin Gabay - Technology Professional...",
"contact": {
"firstName": "Benjamin",
"lastName": "Gabay",
"email": "benjamin@example.com",
"linkedin": "linkedin.com/in/benjamin-gabay",
"location": { "city": "Paris", "country": "FRA" }
},
"profile": {
"domain": "Technology",
"specialization": "Backend Development",
"seniorityLevel": "Senior",
"experienceYears": 8,
"technicalStack": ["Python", "PostgreSQL", "Docker"],
"hasManagement": true
},
"attributes": {
"languages": { "english": 1, "french": 1 },
"technical_skills": { "python_experience": 1 },
"professional_experience": { "total_experience_years": 8 }
}
}
],
"totalResults": 42
}Document Management
List All Documents
curl "https://api.floreal.aiv1/public/documents?limit=20" \
-H "X-API-Key: YOUR_API_KEY"Response:
{
"data": [
{
"documentId": "789e4567-...",
"documentName": "Benjamin Gabay - CV",
"status": "completed",
"documentDate": "11-2025",
"candidate": {
"firstName": "Benjamin",
"lastName": "Gabay",
"fullName": "Benjamin Gabay",
"domain": "Sales Marketing"
},
"timestamps": {
"createdAt": "2025-11-05T14:30:00Z"
}
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 127,
"hasMore": true
},
"summary": {
"completed": 115,
"pending": 5,
"failed": 3,
"total": 127
}
}Delete Document
curl -X DELETE "https://api.floreal.aiv1/public/documents/789e4567-..." \
-H "X-API-Key: YOUR_API_KEY"Removes from:
- Database (document record)
- Cloud Storage (S3 file)
- Vector Database (search embeddings)
Complete Integration Example
const BASE_URL = "https://api.floreal.ai";
const API_KEY = "YOUR_API_KEY";
// 1. Upload CV
async function uploadCV(url, name) {
const response = await fetch(
`${BASE_URL}/v1/public/documents/upload-from-url`,
{
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: url,
documentName: name,
documentDate: "11-2025",
}),
}
);
const { documentId } = await response.json();
console.log("✅ Upload started:", documentId);
// Wait for processing
return await waitForProcessing(documentId);
}
// 2. Wait for processing
async function waitForProcessing(documentId) {
for (let i = 0; i < 18; i++) {
const response = await fetch(
`${BASE_URL}/v1/public/documents/${documentId}`,
{
headers: { "X-API-Key": API_KEY },
}
);
const data = await response.json();
if (data.status === "completed") {
console.log("✅ Processing complete!");
return data;
}
if (data.status === "failed") {
throw new Error(`Failed: ${data.error?.message}`);
}
console.log(`⏳ ${data.status}...`);
await new Promise((r) => setTimeout(r, 5000));
}
throw new Error("Timeout");
}
// 3. Search for candidates
async function searchCandidates(query) {
// Start search
const searchResponse = await fetch(`${BASE_URL}/v1/public/searches/dense`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ query, top_k: 20 }),
});
const { searchId } = await searchResponse.json();
console.log("🔍 Search started:", searchId);
// Wait for completion
for (let i = 0; i < 20; i++) {
const statusResponse = await fetch(
`${BASE_URL}/v1/public/searches/${searchId}`,
{
headers: { "X-API-Key": API_KEY },
}
);
const status = await statusResponse.json();
if (status.status === "completed") {
// Get results
const resultsResponse = await fetch(
`${BASE_URL}/v1/public/searches/${searchId}/results`,
{
headers: { "X-API-Key": API_KEY },
}
);
const results = await resultsResponse.json();
console.log(`✅ Found ${results.totalResults} candidates`);
return results;
}
if (status.status === "failed") {
throw new Error(`Search failed: ${status.error}`);
}
await new Promise((r) => setTimeout(r, 3000));
}
throw new Error("Search timeout");
}
// Usage
async function main() {
// Upload CVs
const cv1 = await uploadCV(
"https://example.com/john-doe.pdf",
"John Doe - Software Engineer"
);
console.log("Contact:", cv1.contact);
console.log("Profile:", cv1.profile);
// Search
const results = await searchCandidates("experienced Python developer");
results.results.forEach((candidate) => {
console.log(`${candidate.contact.fullName} - Score: ${candidate.score}`);
});
}
main();import requests
import time
BASE_URL = 'https://api.floreal.ai'
API_KEY = 'YOUR_API_KEY'
def upload_cv(url, name):
"""Upload CV and wait for processing"""
# Start upload
response = requests.post(
f'{BASE_URL}/v1/public/documents/upload-from-url',
headers={
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
json={
'url': url,
'documentName': name,
'documentDate': '11-2025'
}
)
document_id = response.json()['documentId']
print(f'✅ Upload started: {document_id}')
# Wait for processing
for i in range(18):
status_response = requests.get(
f'{BASE_URL}/v1/public/documents/{document_id}',
headers={'X-API-Key': API_KEY}
)
data = status_response.json()
if data['status'] == 'completed':
print('✅ Processing complete!')
return data
if data['status'] == 'failed':
raise Exception(f"Failed: {data.get('error', {}).get('message')}")
print(f"⏳ {data['status']}...")
time.sleep(5)
raise Exception('Timeout')
def search_candidates(query):
"""Search for candidates"""
# Start search
search_response = requests.post(
f'{BASE_URL}/v1/public/searches/dense',
headers={
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
json={'query': query, 'top_k': 20}
)
search_id = search_response.json()['searchId']
print(f'🔍 Search started: {search_id}')
# Wait for completion
for i in range(20):
status_response = requests.get(
f'{BASE_URL}/v1/public/searches/{search_id}',
headers={'X-API-Key': API_KEY}
)
status = status_response.json()
if status['status'] == 'completed':
# Get results
results_response = requests.get(
f'{BASE_URL}/v1/public/searches/{search_id}/results',
headers={'X-API-Key': API_KEY}
)
results = results_response.json()
print(f"✅ Found {results['totalResults']} candidates")
return results
if status['status'] == 'failed':
raise Exception(f"Search failed: {status.get('error')}")
time.sleep(3)
raise Exception('Search timeout')
# Usage
if __name__ == '__main__':
# Upload CV
cv = upload_cv(
'https://example.com/john-doe.pdf',
'John Doe - Software Engineer'
)
print('Contact:', cv['contact'])
print('Profile:', cv['profile'])
# Search
results = search_candidates('experienced Python developer')
for candidate in results['results']:
print(f"{candidate['contact']['fullName']} - Score: {candidate['score']}")Quick Reference
Upload Methods Comparison
| Method | Complexity | Requests | Best For |
|---|---|---|---|
| URL | ⭐ Simple | 1 | Automation, webhooks, files already hosted |
| Direct | ⭐ Simple | 1 | Quick integration, local files |
| Presigned | ⭐⭐⭐ Complex | 3 | Large files, browser uploads |
Search Algorithms Comparison
| Algorithm | Speed | Quality | Best For |
|---|---|---|---|
| Dense | 5-10s | Good | Natural language, concepts |
| Sparse | 5-10s | Good | Keywords, technical skills |
| Hybrid | 15-30s | Best | Maximum quality, complex queries |
| Builtin-Rerank | 10-20s | Better | Middle ground |
Processing Times
| Operation | Duration |
|---|---|
| CV Upload | 0-15s |
| CV Processing | 30-90s |
| Dense/Sparse Search | 5-10s |
| Hybrid Search | 15-30s |
| Builtin-Rerank Search | 10-20s |
Best Practices
Uploads
- ✅ Use URL upload for automation and webhooks
- ✅ Validate file size (max 10MB) before uploading
- ✅ Store documentId in your database for reference
- ✅ Handle failed uploads gracefully with retries
- ✅ Use direct download URLs (not sharing links)
Polling
- ✅ Poll every 5 seconds for CV processing
- ✅ Poll every 3 seconds for search results
- ✅ Set reasonable timeouts (90s for CVs, 60s for searches)
- ✅ Handle both completed and failed states
Searching
- ✅ Start with Dense search for most queries
- ✅ Use Sparse for exact keyword matching
- ✅ Use Hybrid when quality matters most
- ✅ Cache search results - they don't change
- ✅ Filter results by score (>0.7 for high quality)
Data Management
- ✅ Store documentId and searchId in your database
- ✅ Use extractedText for custom analysis
- ✅ Parse attributes for advanced filtering
- ✅ Delete outdated CVs to maintain clean database
Next Steps
Upload CVs
Deep dive into CV upload methods
Manage Documents
Learn about document management
Search API
Master semantic and keyword search
API Reference
Complete endpoint documentation
Support
Need help? We're here to assist:
- 💬 Chat: Available in Discord
- 📚 Docs: Full documentation available
- 🐛 Issues: Report bugs via support