### Example output of a job search Source: https://hossam-elshabory.github.io/pywuzzuf Shows the expected console output format when running the search example. ```text Found 5 jobs. [12345] Senior Python Developer Company: TechCorp [67890] Backend Engineer Company: StartupX ⚠️ Quality Warning: ['company'] ... ``` -------------------------------- ### Install PyWuzzuf using pip Source: https://hossam-elshabory.github.io/pywuzzuf/installation Install PyWuzzuf using the standard pip package installer. ```bash pip install pywuzzuf ``` -------------------------------- ### Install Build Dependencies on Debian/Ubuntu Source: https://hossam-elshabory.github.io/pywuzzuf/installation For Debian/Ubuntu systems, install GCC and Python development headers required for building packages. ```bash sudo apt-get update sudo apt-get install build-essential python3-dev ``` -------------------------------- ### Install Xcode Command Line Tools Source: https://hossam-elshabory.github.io/pywuzzuf/installation On macOS, install the Xcode Command Line Tools to satisfy build dependencies. ```bash xcode-select --install ``` -------------------------------- ### Production Wrapper Best Practice Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Example of using a try/except block with WuzzufAPIError for robust error handling in production scripts. ```APIDOC ## The Production Wrapper ### Description For robust scripts, wrap your entry point in a `try/except` block that catches `WuzzufAPIError`. This ensures that network blips or API changes don't crash your entire pipeline unexpectedly. ### Example Usage ```python import asyncio import logging from pywuzzuf import WuzzufClient, WuzzufAPIError logging.basicConfig(level=logging.INFO) async def fetch_jobs(): async with WuzzufClient() as client: return await client.jobs.search("Python").limit(100).all() async def main(): try: jobs = await fetch_jobs() logging.info(f"Successfully processed {len(jobs.items)} jobs.") except WuzzufAPIError as e: logging.error(f"Failed to fetch jobs: {e}") # Implement retry logic or alerting here if __name__ == "__main__": asyncio.run(main()) ``` ``` -------------------------------- ### Install Build Dependencies on Alpine Linux Source: https://hossam-elshabory.github.io/pywuzzuf/installation On Alpine Linux, install GCC, musl development headers, Python development headers, and libffi development headers. ```bash apk add gcc musl-dev python3-dev libffi-dev ``` -------------------------------- ### Verify PyWuzzuf Installation Source: https://hossam-elshabory.github.io/pywuzzuf/installation Run this Python command to confirm that PyWuzzuf and its dependencies are installed correctly. It checks the import and prints the version without making network requests. ```python python -c "import pywuzzuf; print(f'PyWuzzuf v{pywuzzuf.__version__} loaded successfully.')" ``` -------------------------------- ### WuzzufClient Initialization (Async) Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Demonstrates the recommended way to initialize and use the WuzzufClient using an asynchronous context manager to ensure proper session closure. ```APIDOC ## WuzzufClient Initialization (Async) ### Description Use the `async with` statement to initialize the `WuzzufClient`. This ensures that the underlying `curl_cffi` session is correctly closed, preventing resource leaks. ### Method Asynchronous Context Manager ### Endpoint N/A ### Request Example ```python from pywuzzuf import WuzzufClient async with WuzzufClient() as client: # Session is active and impersonation headers are set pass # Session is closed automatically ``` ``` -------------------------------- ### SyncWuzzufClient Initialization (Synchronous) Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Shows how to initialize and use the synchronous `SyncWuzzufClient` with a context manager for automatic cleanup. ```APIDOC ## SyncWuzzufClient Initialization (Synchronous) ### Description Initialize the `SyncWuzzufClient` using a `with` statement. This client manages its own private event loop and ensures cleanup upon exiting the context. ### Method Synchronous Context Manager ### Endpoint N/A ### Request Example ```python from pywuzzuf import SyncWuzzufClient with SyncWuzzufClient() as client: # Client manages a private event loop pass # Background loop and session are cleaned up ``` ### Note Avoid using `SyncWuzzufClient` within an already running asyncio loop (e.g., Jupyter Notebooks, FastAPI). Use `WuzzufClient` in such environments. ``` -------------------------------- ### Perform an asynchronous job search with PyWuzzuf Source: https://hossam-elshabory.github.io/pywuzzuf Demonstrates initializing the client, searching for jobs, accessing attributes safely, and checking for data quality anomalies. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: # 1. Search for recent Python jobs result = await client.jobs.search("Python Developer").limit(5).all() print(f"Found {len(result.items)} jobs.\n") for job in result.items: # 2. Safe attribute access title = job.attributes.title company = job.company.attributes.name if job.company else "Unknown" print(f"[{job.id}] {title}") print(f" Company: {company}") # 3. Check data quality if job.quality.has_anomalies: print(f" ⚠️ Quality Warning: {job.quality.missing_fields}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Manage Client Lifecycle Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Use context managers to ensure sessions are properly closed and resources are released. ```python from pywuzzuf import WuzzufClient async with WuzzufClient() as client: # Session is active and impersonation headers are set pass # Session is closed automatically ``` ```python from pywuzzuf import SyncWuzzufClient with SyncWuzzufClient() as client: # Client manages a private event loop pass # Background loop and session are cleaned up ``` -------------------------------- ### Basic Synchronous Search with PyWuzzuf Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Initialize the synchronous client and perform a basic job search. Handles pagination and session management automatically. Fetches the first 5 results for 'Python Developer'. ```python from pywuzzuf import SyncWuzzufClient with SyncWuzzufClient() as client: print("🔍 Searching for 'Python Developer'...") # Fetch the first 5 results result = client.jobs.search("Python Developer").limit(5).all() for job in result.items: print(f"- {job.attributes.title}") ``` -------------------------------- ### Running a Python Script with uv Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Command to run a Python script named `main.py` using the `uv` ASGI server, suitable for asynchronous applications. ```bash uv run main.py ``` -------------------------------- ### Initialize SearchFilters Source: https://hossam-elshabory.github.io/pywuzzuf/usage/filters Demonstrates creating an immutable SearchFilters instance. Once instantiated, the object cannot be modified to prevent race conditions. ```python from pywuzzuf import SearchFilters # Create a filter instance filters = SearchFilters(country_id=1) # filters.country_id = 2 <- Raises FrozenInstanceError ``` -------------------------------- ### Basic Asynchronous Search with PyWuzzuf Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Initialize the asynchronous client and perform a basic job search. Handles pagination and session management automatically. Fetches the first 5 results for 'Python Developer'. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: print("🔍 Searching for 'Python Developer'...") # Fetch the first 5 results result = await client.jobs.search("Python Developer").limit(5).all() for job in result.items: print(f"- {job.attributes.title}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Client Configuration Options Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Details the configurable parameters for the WuzzufClient, including base URL, browser impersonation, and custom sessions. ```APIDOC ## Client Configuration ### Parameters | Parameter | Type | Default | Description | |----------------|--------|---------------------|---------------------------------------------------| | `base_url` | `str` | ` ``` -------------------------------- ### Fetch Jobs Eagerly with .all() Source: https://hossam-elshabory.github.io/pywuzzuf/usage/pagination Use `.all()` to fetch all results at once. This method is suitable for smaller datasets. Always set a `limit` to prevent excessive memory usage. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: # Fetch up to 100 jobs result = await client.jobs.search("Data Science").limit(100).all() print(f"Fetched: {len(result.items)}") print(f"Pages: {result.pages_fetched}") print(f"Stopped Early: {result.terminated_early}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Set Hard Caps for Eager Collection Source: https://hossam-elshabory.github.io/pywuzzuf/usage/pagination When using `.all()`, set both `limit` and `max_pages` to control the maximum number of items and pages fetched, preventing runaway queries and memory issues. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: result = await client.jobs.search("Python") \ .limit(500) \ .max_pages(20) \ .all() print(f"Total jobs fetched: {len(result.items)}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Search Jobs with Query Builder Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Chain filters and limits on the jobs resource to construct and execute complex searches. ```python # 1. Initialize the query (No network request yet) query = client.jobs.search("Python Developer") # 2. Chain filters and limits result = await query \ .filter(SearchFilters(posted_within=DateRange.LAST_WEEK)) \ .limit(20) \ .all() # 3. Iterate results for job in result.items: print(job.attributes.title) ``` -------------------------------- ### Add PyWuzzuf using uv Source: https://hossam-elshabory.github.io/pywuzzuf/installation Use the 'uv' package manager to add PyWuzzuf to your project. 'uv' is recommended for faster dependency resolution. ```bash uv add pywuzzuf ``` -------------------------------- ### Running a Python Script with Python Interpreter Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Standard command to execute a Python script named `main.py` using the default Python interpreter. ```bash python main.py ``` -------------------------------- ### Production Wrapper for Fetching Jobs Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Implement a robust production wrapper using a try/except block to catch WuzzufAPIError. This prevents network issues or API changes from crashing the pipeline. Logs errors and provides a placeholder for retry logic or alerting. ```python import asyncio import logging from pywuzzuf import WuzzufClient, WuzzufAPIError logging.basicConfig(level=logging.INFO) async def fetch_jobs(): async with WuzzufClient() as client: return await client.jobs.search("Python").limit(100).all() async def main(): try: jobs = await fetch_jobs() logging.info(f"Successfully processed {len(jobs.items)} jobs.") except WuzzufAPIError as e: logging.error(f"Failed to fetch jobs: {e}") # Implement retry logic or alerting here if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Safe Access Pattern for Job Attributes Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Illustrates a defensive programming approach for accessing potentially missing job attributes like salary and company details, using conditional checks. ```python # Safe access pattern salary = job.attributes.salary if salary and salary.min: print(f"Salary: {salary.min} - {salary.max}") if job.company: print(f"Company: {job.company.attributes.name}") ``` -------------------------------- ### Configure Browser Impersonation Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Update the impersonate parameter to a newer Chrome version if encountering BotDetectionError. ```python # Example: Updating the browser fingerprint async with WuzzufClient(impersonate="chrome124") as client: # ... pass ``` -------------------------------- ### Accessing Company Details Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models The company object may be None if enrichment fails, so always verify its existence. ```python if job.company: attrs = job.company.attributes print(f"Company: {attrs.name}") print(f"Website: {attrs.website}") else: print("Company data not available.") ``` -------------------------------- ### Implement Manual Stopping with on_progress Source: https://hossam-elshabory.github.io/pywuzzuf/usage/pagination Use the `on_progress` callback to monitor pagination and stop iteration based on custom conditions, such as reaching a specific number of fetched items. ```python import asyncio from pywuzzuf import WuzzufClient, PaginationSignal async def main(): def monitor_progress(total, page): print(f"Fetched {total} jobs (Page {page})...") # Custom stop condition if total > 1000: print("Limit reached, stopping.") return PaginationSignal.STOP async with WuzzufClient() as client: async for job in client.jobs.search("Sales").on_progress(monitor_progress).paginate(): print(f"Job: {job.attributes.title}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Accessing Job Attributes Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Demonstrates how to access core job metadata like title, description, posted date, and location from the `.attributes` property of a job object. ```python job.attributes.title # "Senior Python Developer" job.attributes.description # Full HTML description job.attributes.posted_at # datetime object (UTC) job.attributes.location # Location object (City, Country) ``` -------------------------------- ### Handle BotDetectionError Source: https://hossam-elshabory.github.io/pywuzzuf/usage/resilience Demonstrates how to catch and handle BotDetectionError when the Wuzzuf API triggers anti-bot measures. ```python import asyncio from pywuzzuf import WuzzufClient, BotDetectionError async def main(): try: async with WuzzufClient() as client: result = await client.jobs.search("Python").limit(10).all() print(f"Found {len(result.items)} jobs.") except BotDetectionError as e: print(f"Bot detection triggered: {e}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Add PyWuzzuf using Poetry Source: https://hossam-elshabory.github.io/pywuzzuf/installation If you are using Poetry for dependency management, add PyWuzzuf to your project with this command. ```bash poetry add pywuzzuf ``` -------------------------------- ### Accessing Salary Information Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Salary data is often partially filled; verify the existence of the salary object and its fields before access. ```python 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.") ``` -------------------------------- ### Asynchronous Filtering with SearchFilters Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Applies filters to narrow down search results, specifically finding jobs posted within the last 24 hours using `SearchFilters` and `DateRange.LAST_24_HOURS`. ```python from pywuzzuf import SearchFilters, DateRange filters = SearchFilters(posted_within=DateRange.LAST_24_HOURS) result = await client.jobs.search("DevOps").filter(filters).all() print(f"Found {len(result.items)} recent DevOps jobs.") ``` -------------------------------- ### Accessing Enriched Company Data Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Shows how to access the pre-fetched company profile, including name and website, directly attached to the job object via the `.company` property. ```python if job.company: print(f"Company: {job.company.attributes.name}") print(f"Website: {job.company.attributes.website}") ``` -------------------------------- ### Upgrade pip Source: https://hossam-elshabory.github.io/pywuzzuf/installation If you encounter build errors related to 'curl_cffi', upgrade your pip version as older versions might not find the correct binary wheels. ```bash pip install --upgrade pip ``` -------------------------------- ### Stream Large Datasets with async for Source: https://hossam-elshabory.github.io/pywuzzuf/usage/pagination Utilize the asynchronous iterator with `async for` for memory-efficient processing of large datasets. This method keeps only one page in memory at a time. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: async for job in client.jobs.search("DevOps").paginate(): # Process one job at a time print(f"Processing: {job.attributes.title}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Update Browser Impersonation Source: https://hossam-elshabory.github.io/pywuzzuf/usage/resilience Shows how to update the browser fingerprint to mitigate bot detection by specifying a different impersonation target. ```python # Try a newer Chrome version or a different browser async with WuzzufClient(impersonate="chrome124") as client: # ... pass ``` -------------------------------- ### Implement Pagination Error Handling Source: https://hossam-elshabory.github.io/pywuzzuf/usage/resilience Uses the on_error callback to define custom logic for skipping failed pages during large data fetches. ```python import asyncio from pywuzzuf import WuzzufClient, PaginationSignal async def main(): def handle_error(error, page, total_fetched): print(f"⚠️ Error on page {page}: {error}") # If we hit a 404 or Timeout, skip the page if "404" in str(error) or "Timeout" in str(error): return PaginationSignal.CONTINUE # Stop on critical errors return PaginationSignal.STOP async with WuzzufClient() as client: result = await client.jobs.search("Backend") \ .on_error(handle_error) \ .limit(500) \ .all() print(f"Fetched {len(result.items)} items.") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Filter by Location and Metadata Source: https://hossam-elshabory.github.io/pywuzzuf/usage/filters Applies filters using internal Wuzzuf IDs for country, city, and career level. ```python filters = SearchFilters( country_id=1, # e.g., Egypt city_id=1545, # e.g., Cairo career_level_id=3 # e.g., Experienced ) ``` -------------------------------- ### Synchronous Filtering with SearchFilters Source: https://hossam-elshabory.github.io/pywuzzuf/quickstart Applies filters to narrow down search results, specifically finding jobs posted within the last 24 hours using `SearchFilters` and `DateRange.LAST_24_HOURS`. ```python from pywuzzuf import SearchFilters, DateRange filters = SearchFilters(posted_within=DateRange.LAST_24_HOURS) result = client.jobs.search("DevOps").filter(filters).all() print(f"Found {len(result.items)} recent DevOps jobs.") ``` -------------------------------- ### Set Custom Base URL Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Override the default API endpoint to point to a mock server or test fixture. ```python async with WuzzufClient(base_url="http://localhost:8080/api") as client: # Requests go to local mock server pass ``` -------------------------------- ### Handle Errors During Pagination Source: https://hossam-elshabory.github.io/pywuzzuf/usage/pagination Implement an `on_error` callback to customize error handling. Return `PaginationSignal.CONTINUE` to skip a failed page or `PaginationSignal.STOP` to halt the process. ```python import asyncio from pywuzzuf import WuzzufClient, PaginationSignal async def main(): def handle_error(error, page, total_fetched): print(f"Error on page {page}: {error}") # If it's a 404, skip the page. Otherwise, stop. if "404" in str(error): return PaginationSignal.CONTINUE return PaginationSignal.STOP async with WuzzufClient() as client: result = await client.jobs.search("Backend") \ .on_error(handle_error) \ .all() print(f"Finished with {len(result.items)} jobs.") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Apply Relative Time Windows Source: https://hossam-elshabory.github.io/pywuzzuf/usage/filters Uses DateRange for sliding time windows relative to the current UTC time. The timestamp is resolved at the moment of instantiation. ```python from pywuzzuf import SearchFilters, DateRange # Jobs posted in the last 24 hours filters = SearchFilters(posted_within=DateRange.LAST_24_HOURS) # Jobs posted in the last month filters = SearchFilters(posted_within=DateRange.LAST_MONTH) ``` -------------------------------- ### Inject Custom Session with Proxy Source: https://hossam-elshabory.github.io/pywuzzuf/usage/client Inject a pre-configured curl_cffi session to route traffic through a proxy. The caller is responsible for closing the session manually. ```python from curl_cffi.requests import AsyncSession from pywuzzuf import WuzzufClient # 1. Configure a session with proxies session = AsyncSession( impersonate="chrome124", proxy="http://user:pass@proxy.example.com:8080" ) # 2. Inject the session into the client async with WuzzufClient(session=session) as client: # All requests now use the configured proxy results = await client.jobs.search("Python").limit(5).all() await session.close() ``` -------------------------------- ### RateLimitError Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Raised when HTTP 429 is encountered after retries, indicating that the rate limit has been exceeded. ```APIDOC ## RateLimitError ### Description Raised when HTTP 429 is encountered after retries. The client automatically retries on `429` responses with exponential back-off. If the limit persists after 5 attempts, this exception is raised. ### Attributes - `status_code` (int) - Always `429`. ### Action Wait a few minutes before restarting your script. ``` -------------------------------- ### Safe Access Pattern for Nullable Fields Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Use explicit checks before accessing nested attributes to prevent runtime errors when fields are None. ```python # ❌ 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") ``` -------------------------------- ### Checking Data Quality Anomalies Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Use the quality attribute to identify and filter out jobs with missing or degraded data. ```python if job.quality.has_anomalies: # Skip jobs with critical missing data if "company" in job.quality.missing_fields: continue ``` -------------------------------- ### Handle Rate Limit Error Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Catch RateLimitError when HTTP 429 is encountered after retries. The client automatically retries on 429 responses with exponential back-off. If the limit persists after 5 attempts, this exception is raised. Action: Wait a few minutes before restarting your script. ```python from pywuzzuf import RateLimitError try: # ... search logic ... except RateLimitError as e: print(f"Rate limit exceeded: {e}") ``` -------------------------------- ### Apply Absolute Time Windows Source: https://hossam-elshabory.github.io/pywuzzuf/usage/filters Uses AbsoluteDateFilter for fixed date boundaries. Requires timezone-aware datetime objects to avoid warnings. ```python from datetime import datetime, timezone from pywuzzuf import SearchFilters, AbsoluteDateFilter # Define a fixed window q1_filter = AbsoluteDateFilter( since=datetime(2024, 1, 1, tzinfo=timezone.utc), until=datetime(2024, 3, 31, tzinfo=timezone.utc) ) filters = SearchFilters(posted_within=q1_filter) ``` -------------------------------- ### Enforce Keyword Constraints Source: https://hossam-elshabory.github.io/pywuzzuf/usage/filters Applies strict keyword filtering to search results, ensuring returned jobs contain the specified terms. ```python # Only return jobs that explicitly contain "Django" AND "AWS" filters = SearchFilters(keywords=("Django", "AWS")) result = await client.jobs.search("Python").filter(filters).all() ``` -------------------------------- ### Accessing Job Location Data Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Location fields like city and area are optional and require conditional checks. ```python 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 ``` -------------------------------- ### BotDetectionError Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Raised when repeated HTTP 403 responses are detected, indicating Wuzzuf has flagged the client as a bot. ```APIDOC ## BotDetectionError ### Description Raised when repeated HTTP 403 responses are detected. This error indicates that Wuzzuf has flagged the client as a bot. ### Recommended Resolution 1. **Update Fingerprint**: Change the `impersonate` parameter to a newer browser version (e.g., `"chrome124"`). 2. **Proxies**: Route traffic through a residential proxy. 3. **Slow Down**: Add delays between pagination calls. ### Automatic Retry This error is only raised after **3 consecutive 403 responses**. The client attempts to proceed with retries before finally raising this exception. ### Example Usage ```python from pywuzzuf import BotDetectionError try: # ... search logic ... except BotDetectionError as e: print(f"Blocked by Wuzzuf: {e}") # Recommended Action: Update impersonate string or use proxies ``` ``` -------------------------------- ### Company and Quality Models Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Definitions for the company metadata and the data quality report used to assess record integrity. ```APIDOC ## Company and Quality Models ### CompanyAttributes - **name** (str) - Required. Company name. - **description** (str) - Nullable. Profile text. - **website** (str) - Nullable. Official URL. - **logo** (str) - Nullable. Logo URL. ### DataQualityReport - **missing_fields** (list[str]) - Fields missing due to failed enrichment. - **degraded_fields** (list[str]) - Fields that resolved to None ambiguously. - **has_anomalies** (bool) - True if any anomalies were detected. ``` -------------------------------- ### Job Attributes and Metadata Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models Details regarding the job.attributes object, which contains location, salary, and temporal metadata. ```APIDOC ## Job Attributes ### Location - **country** (LocationCountry) - Required. Contains id, name, code. - **city** (LocationCity) - Nullable. Contains id, name, lat/long. - **area** (LocationArea) - Nullable. Contains id, name, lat/long. ### Salary - **min** (int) - Nullable. Minimum salary value. - **max** (int) - Nullable. Maximum salary value. - **currency** (NamedAttribute) - Nullable. Currency information. - **period** (NamedAttribute) - Nullable. Payment frequency. - **is_paid** (bool) - Required. False for unpaid internships. ### Dates & Metadata - **posted_at** (datetime) - Nullable. UTC timestamp. - **expire_at** (datetime) - Nullable. UTC timestamp. - **keywords** (list[Keyword]) - Required. List of skills/tags. - **vacancies** (int) - Required. Number of open positions. ``` -------------------------------- ### WuzzufAPIError - Base Exception Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions The base class for all PyWuzzuf errors. Recommended for catching all client-side API errors. ```APIDOC ## WuzzufAPIError ### Description Base class for all PyWuzzuf errors. This is the recommended catch-all for production scripts where you want to log errors but keep the pipeline running. ### Attributes - `status_code` (int) - The HTTP status code returned by the API. - `args[0]` (str) - The detailed error message. ### Example Usage ```python import asyncio from pywuzzuf import WuzzufClient, WuzzufAPIError async def main(): try: async with WuzzufClient() as client: result = await client.jobs.search("Python").all() except WuzzufAPIError as e: # Handles RateLimit, BotDetection, and InvalidResponse print(f"API Error (HTTP {e.status_code}): {e}") # Log to sentry/file pass if __name__ == "__main__": asyncio.run(main()) ``` ``` -------------------------------- ### Handle Bot Detection Error Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Catch BotDetectionError when Wuzzuf flags the client as a bot after repeated HTTP 403 responses. Consider updating the impersonate string or using proxies. ```python from pywuzzuf import BotDetectionError try: # ... search logic ... except BotDetectionError as e: print(f"Blocked by Wuzzuf: {e}") # Recommended Action: Update impersonate string or use proxies ``` -------------------------------- ### Handle All PyWuzzuf API Errors Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Catch WuzzufAPIError to handle all client-side API errors, including RateLimit, BotDetection, and InvalidResponse. This is recommended for production scripts to log errors and maintain pipeline execution. ```python import asyncio from pywuzzuf import WuzzufClient, WuzzufAPIError async def main(): try: async with WuzzufClient() as client: result = await client.jobs.search("Python").all() except WuzzufAPIError as e: # Handles RateLimit, BotDetection, and InvalidResponse print(f"API Error (HTTP {e.status_code}): {e}") # Log to sentry/file pass if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Audit Data Quality Source: https://hossam-elshabory.github.io/pywuzzuf/usage/resilience Checks the quality attribute of EnrichedJob objects to identify missing or degraded fields in API responses. ```python import asyncio from pywuzzuf import WuzzufClient async def main(): async with WuzzufClient() as client: result = await client.jobs.search("Data Scientist").limit(10).all() for job in result.items: if job.quality.has_anomalies: print(f"Job {job.id} has issues:") print(f" Missing: {job.quality.missing_fields}") print(f" Degraded: {job.quality.degraded_fields}") if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### EnrichedJob Data Model Source: https://hossam-elshabory.github.io/pywuzzuf/reference/models The EnrichedJob object is the primary data structure returned by search operations, containing job attributes, company information, and a data quality report. ```APIDOC ## EnrichedJob Data Model ### Description The primary object returned by all search operations, bundling core job details with hydrated company data and a data quality report. ### Fields - **id** (str) - Unique job identifier. - **attributes** (JobAttributes) - Core metadata of the job listing. - **company** (CompanyAttributes) - Company metadata (Nullable). - **quality** (DataQualityReport) - Integrity report for the job record. ### Usage Note Always perform defensive checks when accessing nested attributes, as many fields (e.g., company, salary, city) may be None. ``` -------------------------------- ### InvalidResponseError Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Raised when API response validation fails, typically due to schema changes or malformed responses. ```APIDOC ## InvalidResponseError ### Description Raised when API response validation fails. This usually indicates that the Wuzzuf API schema has changed, or the response was malformed (e.g., expecting a `dict` but got a `str`). ### Attributes - `validation_error` (Exception) - The underlying Pydantic validation error. ### Example Usage ```python from pywuzzuf import InvalidResponseError try: # ... search logic ... except InvalidResponseError as e: print(f"Validation failed: {e.validation_error}") ``` ``` -------------------------------- ### Handle Invalid Response Error Source: https://hossam-elshabory.github.io/pywuzzuf/reference/exceptions Catch InvalidResponseError when API response validation fails, often due to schema changes or malformed responses. Access the underlying Pydantic validation error via the `validation_error` attribute. ```python from pywuzzuf import InvalidResponseError try: # ... search logic ... except InvalidResponseError as e: print(f"Validation failed: {e.validation_error}") ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.