Skip to main content

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.

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

Installation

pip install geekflare-api python-decouple
Or with uv:
uv add geekflare-api python-decouple

Set your API key

.env
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:
yourapp/services/geekflare.py
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

yourapp/views.py
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

yourapp/urls.py
from django.urls import path
from .views import ScrapeView

urlpatterns = [
    path("scrape/", ScrapeView.as_view(), name="scrape"),
]
Test:
curl -X POST http://localhost:8000/scrape/ \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://toscrape.com/" }'

View

yourapp/views.py
@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

yourapp/urls.py
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:
curl -X POST http://localhost:8000/search/ \
  -H "Content-Type: application/json" \
  -d '{ "query": "Django REST framework tutorial", "limit": 5 }'

Screenshot

View

yourapp/views.py
@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

yourapp/urls.py
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:
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:
yourapp/views.py
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.
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

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

Next steps