
I built this assistant while helping a sociology graduate student process forty pages of interview transcripts. The goal was to shrink the initial coding phase from several days to under an hour without sacrificing methodological rigor. What follows is a lightweight Python pipeline that performs thematic analysis via an LLM, using Oxlo.ai so that long transcripts incur a flat per-request cost instead of ballooning token fees.
What you'll need
You will need Python 3.10 or newer, the OpenAI SDK installed via pip install openai, and an API key from https://portal.oxlo.ai. Oxlo.ai uses request-based pricing, which is particularly useful for social science workloads because qualitative text is verbose. Whether you send a one-page memo or a twenty-page focus group transcript, the cost per inference call stays flat. You can review the exact tiers at https://oxlo.ai/pricing. You will also need a plain text file named interview.txt containing your raw qualitative data. I used anonymized semi-structured interview transcripts for this example, but survey open-ends or field notes work equally well.
Step 1: Configure the Oxlo.ai client and load the transcript
I keep the API key in an environment variable to avoid leaking credentials in notebooks. The client initialization is a single line because Oxlo.ai exposes a fully OpenAI-compatible endpoint. I also added a small helper to read the transcript into memory. Since Oxlo.ai does not impose cold starts on popular models, the first request after importing the module fires immediately. That matters when you are iterating on a coding scheme and re-running the script dozens of times in an afternoon.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.environ.get("OXLO_API_KEY")
)
def load_transcript(path):
with open(path, "r", encoding="utf-8") as f:
return f.read()
transcript = load_transcript("interview.txt")
print(f"Loaded {len(transcript)} characters.")
Step 2: Define the research assistant system prompt
The system prompt is the most important part of this build. It must constrain the model to behave like a qualitative methodologist, not a general chatbot. I force it to emit a codebook, excerpt assignments, theme narratives, and an analytic memo. This structure mirrors the workflow in traditional CAQDAS packages, but it is generated in seconds. I keep the output format as Markdown so it pastes cleanly into a Word document or a LaTeX markdown converter. You can edit the inclusion criteria or theme count to match your specific methodology.
SYSTEM_PROMPT = """You are a qualitative research methodologist assisting a social scientist. Your task is to perform thematic analysis on raw qualitative text provided by the user.
Follow these rules:
1. First, generate a concise codebook with 3 to 7 codes. Each code must have a name, definition, and inclusion/exclusion criteria.
2. Next, assign excerpts to codes. Present each excerpt as a bullet under its code.
3. Then, group the codes into 2 to 4 overarching themes. Provide a theme label and a 2-sentence narrative for each.
4. Finally, write a short analytic memo (150 words max) summarizing the dominant pattern and one counter-example found in the text.
5. Output everything as clean Markdown with clear headings.
Be rigorous. Ground every claim in specific textual evidence. Do not invent data not present in the transcript."""
Step 3: Build the thematic analysis function
With the prompt locked, the inference logic is straightforward. I use Llama 3.3 70B because it handles long-context instruction following reliably. I set temperature to 0.3 to keep coding consistent across runs. The function accepts the raw transcript text, wraps it in a user message, and returns the generated analysis string. Because Oxlo.ai offers flat per-request pricing, I do not need to truncate or chunk the transcript to save money. I send the full text in one shot, which preserves narrative flow and avoids context fragmentation.
def analyze_transcript(text: str, model: str = "llama-3.3-70b") -> str:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"Analyze the following transcript:\n\n{text}"},
],
temperature=0.3,
)
return response.choices[0].message.content
Step 4: Add a theory mapping layer
Thematic analysis is stronger when tied back to existing literature. I added a second pass that takes the generated analysis and maps it against a theoretical framework. Here I default to Grounded Theory, but you can swap in Symbolic Interactionism, Structuration Theory, or Critical Race Theory depending on your project. I use Qwen 3 32B for this step because its multilingual reasoning is excellent at parsing dense theoretical language. Splitting the workload into two focused prompts, one for empirical coding and one for theoretical framing, keeps hallucination low and interpretive depth high.
def map_to_literature(analysis_text: str, theory: str = "Grounded Theory", model: str = "qwen-3-32b") -> str:
prompt = f"""Given the following thematic analysis, evaluate how the findings relate to {theory}. Identify where the data supports, extends, or contradicts core tenets. Keep the response under 200 words.\n\nThematic Analysis:\n{analysis_text}"""
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "You are a social theory expert. Be concise and specific."},
{"role": "user", "content": prompt},
],
temperature=0.2,
)
return response.choices[0].message.content
Run it
The script below ties everything together. It loads the file, runs the thematic analysis, prints the result, and then prints the theory mapping. When I ran this against a twelve-thousand-character transcript on Oxlo.ai, the entire job completed in under ten seconds. Notice that the cost was the same whether the transcript was one page or twenty, which matters when you are iterating on a coding scheme across a full corpus. The output is ready to be copied into your research appendix or refactored into JSON.
if __name__ == "__main__":
transcript = load_transcript("interview.txt")
print("=== THEMATIC ANALYSIS ===")
analysis = analyze_transcript(transcript)
print(analysis)
print("\n=== THEORY MAPPING ===")
theory_note = map_to_literature(analysis)
print(theory_note)
Example output:
=== THEMATIC ANALYSIS ===
## Codebook
**Code: Work-Life Boundary**
- Definition: Instances where participants describe separating professional obligations from personal time.
- Inclusion: Explicit mentions of "after hours," "weekend emails," or "family dinner."
- Exclusion: General complaints about workload without temporal reference.
**Code: Institutional Trust**
- Definition: References to confidence in organizational leadership or policy.
- Inclusion: Mentions of "transparency," "fairness," or "leadership promises."
- Exclusion: Comments about individual coworkers.
## Themes
### Theme 1: Navigating Boundary Work
Participants consistently describe active strategies for segmenting work from home life. This theme is supported by excerpts coded under Work-Life Boundary and Temporal Autonomy.
### Theme 2: Erosion of Trust
Multiple participants link remote monitoring tools to declining confidence in management. This pattern appears across early and late career respondents.
## Analytic Memo
The dominant pattern across the transcript is a proactive segmentation strategy among remote workers. A notable counter-example appears in Participant 4, who describes intentional integration of work and family routines, suggesting that boundary work is not uniformly protective.
=== THEORY MAPPING ===
The findings align with Straussian grounded theory in that participants engage in conditional matrix reasoning when describing boundary work. However, the data extends existing literature by introducing digital presence as a third space that complicates traditional segmentation models.
Wrap-up
From here, you can extend the pipeline in two directions. First, wrap the output in Pydantic models to enforce structured JSON instead of Markdown, which makes it trivial to feed results into a database or a visualization library like networkx. Second, parallelize the analysis across a corpus by batching transcripts and writing results to disk as individual markdown files. Oxlo.ai handles concurrency well, and the flat per-request pricing keeps the bill predictable even when you scale up to a hundred interviews.

