Data Models Reference¶
This page documents the data structures returned by the PyWuzzuf client. All models are implemented as Pydantic BaseModel classes, providing type safety and IDE autocompletion.
The Root Object: EnrichedJob¶
This is the primary object returned by all search operations. It bundles the core job details with the hydrated company data and a data quality report.
Handling Nullable Data¶
Data Completeness
Job listings depend entirely on the recruiter filling out the job post on Wuzzuf. Many fields may be None, including company, requirements, salary, and city.
Always use defensive checks when accessing nested attributes.
Safe Access Pattern:
# ❌ Unsafe: Will crash if company is None
print(job.company.attributes.name)
# ✅ Safe: Explicit check
if job.company:
print(job.company.attributes.name)
else:
print("Company not disclosed")
Access Paths¶
| Field | Type | Access Path | Nullable? |
|---|---|---|---|
| ID | str |
job.id |
No |
| Title | str |
job.attributes.title |
No |
| Description | str |
job.attributes.description |
No |
| Company Name | str |
job.company.attributes.name |
Yes (via job.company) |
| City | str |
job.attributes.location.city.name |
Yes |
| Posted At | datetime |
job.attributes.posted_at |
Yes |
Job Details¶
The job.attributes object contains the core metadata of the listing.
Location¶
Location is a nested object containing Country, City, and Area.
loc = job.attributes.location
# Country is always present
country = loc.country.name # "Egypt"
# City and Area are optional
city_name = loc.city.name if loc.city else "Remote / Unspecified"
area_name = loc.area.name if loc.area else None
Structure:
| Field | Type | Nullable? | Description |
|---|---|---|---|
country |
LocationCountry |
No | Contains id, name, code. |
city |
LocationCity |
Yes | Contains id, name, lat/long. |
area |
LocationArea |
Yes | Contains id, name, lat/long. |
Salary¶
Salary information is often optional or partially filled by recruiters.
salary = job.attributes.salary
if salary and salary.min:
print(f"Range: {salary.min} - {salary.max}")
# Currency can be a NamedAttribute or a raw str
currency = salary.currency.name if hasattr(salary.currency, 'name') else str(salary.currency)
print(f"Currency: {currency}")
else:
print("Salary not disclosed.")
Structure:
| Field | Type | Nullable? | Description |
|---|---|---|---|
min |
int |
Yes | Minimum salary value. |
max |
int |
Yes | Maximum salary value. |
currency |
NamedAttribute |
Yes | e.g., {id: 1, name: "EGP"}. |
period |
NamedAttribute |
Yes | e.g., {id: 1, name: "Monthly"}. |
is_paid |
bool |
No | False for unpaid internships. |
Dates & Metadata¶
| Field | Type | Access Path | Nullable? | Description |
|---|---|---|---|---|
posted_at |
datetime |
job.attributes.posted_at |
Yes | UTC timestamp. |
expire_at |
datetime |
job.attributes.expire_at |
Yes | UTC timestamp. |
keywords |
list[Keyword] |
job.attributes.keywords |
No | List of skills/tags (can be empty). |
vacancies |
int |
job.attributes.vacancies |
No | Number of open positions. Default: 0. |
Company Details¶
The job.company object provides full company metadata.
Enrichment Failure
This object is None if enrichment failed or the company data was not found in the API. Always check for existence.
if job.company:
attrs = job.company.attributes
print(f"Company: {attrs.name}")
print(f"Website: {attrs.website}")
else:
print("Company data not available.")
Structure (CompanyAttributes):
| Field | Type | Nullable? | Description |
|---|---|---|---|
name |
str |
No | Company name. |
description |
str |
Yes | Profile text. |
website |
str |
Yes | Official URL. |
logo |
str |
Yes | Logo URL. |
Data Quality Report¶
Every EnrichedJob comes with a quality attribute that reports on the integrity of the data. This is useful for filtering out incomplete records.
if job.quality.has_anomalies:
# Skip jobs with critical missing data
if "company" in job.quality.missing_fields:
continue
Report Attributes:
| Field | Type | Description |
|---|---|---|
missing_fields |
list[str] |
Fields missing due to failed enrichment (e.g., company). |
degraded_fields |
list[str] |
Fields that resolved to None ambiguously (e.g., posted_at). |
has_anomalies |
bool |
True if any anomalies were detected. |
Next Steps¶
Learn how to handle data inconsistencies and API errors at scale.