> ## Documentation Index
> Fetch the complete documentation index at: https://docs.geekflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Django

> Integrate Geekflare API into a Django application using a reusable service class, views, and python-decouple for config.

This guide uses the official `geekflare-api` Python SDK inside a Django project. We'll create a reusable service class, wire it into Django views, and keep secrets out of source control with `python-decouple`.

## Prerequisites

* Python **3.8+**
* Django **4.0+**
* A Geekflare API key — [get one free](https://dash.geekflare.com)

## Installation

```bash theme={null}
pip install geekflare-api python-decouple
```

Or with `uv`:

```bash theme={null}
uv add geekflare-api python-decouple
```

## Set your API key

```bash .env theme={null}
GEEKFLARE_API_KEY=your_api_key_here
```

`python-decouple` reads from `.env` automatically. Add `.env` to `.gitignore`.

***

## Service class

Keep all Geekflare logic in one place. Create `services/geekflare.py` in your app:

```python yourapp/services/geekflare.py theme={null}
from decouple import config
from geekflare_api.client import GeekflareClient
from geekflare_api.models import WebScrapeDto, SearchDto, ScreenshotDto
from geekflare_api.exceptions import ApiException


class GeekflareService:
    """Thin wrapper around the Geekflare SDK for use in Django views."""

    def __init__(self):
        self._api_key = config("GEEKFLARE_API_KEY")

    def _client(self) -> GeekflareClient:
        return GeekflareClient(api_key=self._api_key)

    def web_scrape(self, url: str, **kwargs) -> dict:
        with self._client() as client:
            return client.web_scrape(WebScrapeDto(url=url, **kwargs))

    def search(self, query: str, **kwargs) -> dict:
        with self._client() as client:
            return client.search(SearchDto(query=query, **kwargs))

    def screenshot(self, url: str, **kwargs) -> dict:
        with self._client() as client:
            return client.screenshot(ScreenshotDto(url=url, **kwargs))


# Module-level singleton — import this in your views
geekflare = GeekflareService()
```

***

## Web Scraping

### View

```python yourapp/views.py theme={null}
import json
from django.http import JsonResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from geekflare_api.exceptions import ApiException
from .services.geekflare import geekflare


@method_decorator(csrf_exempt, name="dispatch")
class ScrapeView(View):
    def post(self, request):
        try:
            body = json.loads(request.body)
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON"}, status=400)

        url = body.get("url")
        if not url:
            return JsonResponse({"error": "url is required"}, status=400)

        try:
            result = geekflare.web_scrape(
                url=url,
                renderJS=body.get("renderJS", True),
                blockAds=body.get("blockAds", True),
                format=body.get("format", "html,markdown"),
            )
            return JsonResponse(result, safe=False)

        except ApiException as e:
            return JsonResponse(
                {"error": e.reason, "code": e.status},
                status=e.status or 500,
            )
```

### URL

```python yourapp/urls.py theme={null}
from django.urls import path
from .views import ScrapeView

urlpatterns = [
    path("scrape/", ScrapeView.as_view(), name="scrape"),
]
```

**Test:**

```bash theme={null}
curl -X POST http://localhost:8000/scrape/ \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://toscrape.com/" }'
```

***

## Search

### View

```python yourapp/views.py theme={null}
@method_decorator(csrf_exempt, name="dispatch")
class SearchView(View):
    def post(self, request):
        try:
            body = json.loads(request.body)
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON"}, status=400)

        query = body.get("query")
        if not query:
            return JsonResponse({"error": "query is required"}, status=400)

        try:
            result = geekflare.search(
                query=query,
                limit=body.get("limit", 10),
                location=body.get("location", "us"),
                source=body.get("source", "web"),
                time=body.get("time"),
                format="json",
            )
            return JsonResponse(result, safe=False)

        except ApiException as e:
            return JsonResponse(
                {"error": e.reason, "code": e.status},
                status=e.status or 500,
            )
```

### URL

```python yourapp/urls.py theme={null}
from django.urls import path
from .views import ScrapeView, SearchView

urlpatterns = [
    path("scrape/", ScrapeView.as_view(), name="scrape"),
    path("search/", SearchView.as_view(), name="search"),
]
```

**Test:**

```bash theme={null}
curl -X POST http://localhost:8000/search/ \
  -H "Content-Type: application/json" \
  -d '{ "query": "Django REST framework tutorial", "limit": 5 }'
```

***

## Screenshot

### View

```python yourapp/views.py theme={null}
@method_decorator(csrf_exempt, name="dispatch")
class ScreenshotView(View):
    def post(self, request):
        try:
            body = json.loads(request.body)
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON"}, status=400)

        url = body.get("url")
        if not url:
            return JsonResponse({"error": "url is required"}, status=400)

        try:
            result = geekflare.screenshot(
                url=url,
                type=body.get("type", "png"),
                fullPage=body.get("fullPage", True),
                device=body.get("device", "desktop"),
                viewportWidth=body.get("viewportWidth", 1280),
                viewportHeight=body.get("viewportHeight", 800),
                blockAds=body.get("blockAds", True),
                hideCookie=body.get("hideCookie", True),
                quality=body.get("quality", 90),
            )
            return JsonResponse(result, safe=False)

        except ApiException as e:
            return JsonResponse(
                {"error": e.reason, "code": e.status},
                status=e.status or 500,
            )
```

### URL

```python yourapp/urls.py theme={null}
from django.urls import path
from .views import ScrapeView, SearchView, ScreenshotView

urlpatterns = [
    path("scrape/", ScrapeView.as_view(), name="scrape"),
    path("search/", SearchView.as_view(), name="search"),
    path("screenshot/", ScreenshotView.as_view(), name="screenshot"),
]
```

**Test:**

```bash theme={null}
curl -X POST http://localhost:8000/screenshot/ \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com", "fullPage": true }'
```

***

## Using Django REST Framework

If your project uses DRF, swap the class-based views for `APIView`:

```python yourapp/views.py theme={null}
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from geekflare_api.exceptions import ApiException
from .services.geekflare import geekflare


class ScrapeAPIView(APIView):
    def post(self, request):
        url = request.data.get("url")
        if not url:
            return Response({"error": "url is required"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            result = geekflare.web_scrape(
                url=url,
                renderJS=request.data.get("renderJS", True),
                blockAds=request.data.get("blockAds", True),
                format=request.data.get("format", "html,markdown"),
            )
            return Response(result)

        except ApiException as e:
            return Response(
                {"error": e.reason, "code": e.status},
                status=e.status or status.HTTP_500_INTERNAL_SERVER_ERROR,
            )
```

***

## Error handling

The SDK raises `ApiException` with a `status` code and `reason` string on non-2xx responses.

```python theme={null}
from geekflare_api.exceptions import ApiException

try:
    result = geekflare.web_scrape(url="https://toscrape.com/")
except ApiException as e:
    if e.status == 401:
        # Invalid or missing API key
    elif e.status == 402:
        # Credits exhausted — check dash.geekflare.com
    elif e.status == 403:
        # Endpoint not available on current plan
    elif e.status == 429:
        # Rate limit hit — implement exponential backoff
    else:
        # Unexpected error
        raise
```

### Common error codes

| Code  | Meaning                           |
| ----- | --------------------------------- |
| `401` | Missing or invalid `x-api-key`    |
| `402` | API credits exhausted             |
| `403` | Endpoint requires a higher plan   |
| `404` | Wrong endpoint URL or HTTP method |
| `429` | Rate limit exceeded               |

***

## Next steps

* [PHP quickstart](/api/quickstart/php) — Native cURL and Guzzle
* [API Reference](/api/endpoint/reference) — Full endpoint documentation
* [SDK on PyPI](https://pypi.org/project/geekflare-api/)
