What You'll Build
By the end of this tutorial you'll have a reusable Python client for the Google Scholar API, with search, pagination, error handling, and typed result parsing — ready to drop into a research app, dashboard, or AI pipeline.
We use BitLore because there is no official Google Scholar API from Google. BitLore returns structured JSON in under 100ms.
Prerequisites
Step 1: Store Your API Key Securely
Create a .env file (never commit this to git):
.env
BITLORE_API_KEY=your_api_key_here
Step 2: Build a Scholar Client Class
scholar_client.py
import os
import requests
from dataclasses import dataclass
from typing import Optional
from dotenv import load_dotenv
load_dotenv()
@dataclass
class ScholarPaper:
title: str
link: str
snippet: str
authors: Optional[str] = None
cited_by: Optional[int] = None
class BitLoreScholarClient:
BASE_URL = "https://api.bitlore.in/search"
def __init__(self, api_key: Optional[str] = None):
self.api_key = api_key or os.environ["BITLORE_API_KEY"]
def search(
self,
query: str,
page: int = 1,
timeout: int = 30,
) -> list[ScholarPaper]:
resp = requests.get(
self.BASE_URL,
params={
"search_engine": "google_scholar",
"q": query,
"page": page,
"api_key": self.api_key,
},
timeout=timeout,
)
resp.raise_for_status()
payload = resp.json()
papers = []
for item in payload.get("data", {}).get("results", []):
papers.append(ScholarPaper(
title=item.get("title", ""),
link=item.get("link", ""),
snippet=item.get("snippet", ""),
authors=item.get("authors"),
cited_by=item.get("cited_by"),
))
return papers
Step 3: Run Your First Search
main.py
from scholar_client import BitLoreScholarClient
client = BitLoreScholarClient()
papers = client.search("large language models")
for i, paper in enumerate(papers, 1):
print(f"{i}. {paper.title}")
print(f" {paper.link}\n")
Step 4: Handle Errors Gracefully
Python
import requests
def safe_search(client, query: str, retries: int = 3):
for attempt in range(retries):
try:
return client.search(query)
except requests.HTTPError as e:
if e.response.status_code == 429:
wait = 2 ** attempt
print(f"Rate limited. Retrying in {wait}s...")
time.sleep(wait)
elif e.response.status_code == 401:
raise ValueError("Invalid API key") from e
else:
raise
return []
Step 5: Paginate Through Results
Python
def search_all_pages(client, query: str, max_pages: int = 10):
all_papers = []
for page in range(1, max_pages + 1):
batch = client.search(query, page=page)
if not batch:
break
all_papers.extend(batch)
return all_papers
Response Fields Reference
Each result typically includes:
•
title — paper or article title
•
link — URL to the Scholar result
•
snippet — abstract excerpt or description
•
authors — author list when available
•
cited_by — citation count when available
See the full field reference in our API docs.
Async Version (FastAPI / asyncio)
Python (httpx)
import httpx
async def search_scholar_async(query: str, api_key: str) -> dict:
async with httpx.AsyncClient() as client:
r = await client.get(
"https://api.bitlore.in/search",
params={
"search_engine": "google_scholar",
"q": query,
"api_key": api_key,
},
)
r.raise_for_status()
return r.json()
What's Next?
Combine this client with:
Tags
Google Scholar APIPythonAPI TutorialAcademic SearchREST API