Skip to main content

Searching FDA 483 Observations

Learn how to search and analyze FDA 483 observations using CTWise Intelligence API's powerful semantic search capabilities.

Introduction

FDA 483 observations are issued at the conclusion of facility inspections, documenting conditions that may constitute violations of FDA regulations. CTWise Intelligence provides:

  • Semantic Search: Natural language queries powered by Amazon Titan v2 embeddings
  • Comprehensive Coverage: 25,000+ citation records across 8,100+ facilities
  • Filterable Results: Filter by fiscal year, program area, FEI number, and CFR section
  • Regulatory Context: Automatic CFR cross-referencing and risk scoring

Basic Search: Natural Language Query

The simplest way to search is with a natural language query.

Example: Find Data Integrity Issues

curl -X POST "https://api.ctwise.ai/v1/483/observations/search" \
-H "X-Api-Key: $CTWISE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "data integrity failures in electronic records",
"top_k": 10
}'

Response

{
"query": "data integrity failures in electronic records",
"total": 10,
"results": [
{
"citation_id": "CIT-2025-12345",
"inspection_id": "INS-2025-67890",
"fei_number": "1000234567",
"legal_name": "EXAMPLE PHARMA MANUFACTURING LLC",
"city": "PRINCETON",
"state": "NJ",
"act_cfr_number": "21 CFR 211.180",
"short_description": "The firm failed to maintain complete data to show that all procedures were followed. Electronic records showed evidence of modification without audit trails...",
"inspection_end_date": "2025-11-15",
"fiscal_year": 2026,
"program_area": "Human Drugs",
"similarity_score": 0.94,
"confidence_level": "high"
},
{
"citation_id": "CIT-2025-23456",
"inspection_id": "INS-2025-78901",
"fei_number": "1000345678",
"legal_name": "BIOSCIENCE LABORATORIES INC",
"city": "SAN DIEGO",
"state": "CA",
"act_cfr_number": "21 CFR 211.68",
"short_description": "Procedures for validation of computer systems used in production were inadequate. Audit trail review was not documented...",
"inspection_end_date": "2025-09-22",
"fiscal_year": 2025,
"program_area": "Human Drugs",
"similarity_score": 0.91,
"confidence_level": "high"
}
],
"query_metadata": {
"execution_time_ms": 450,
"index": "483-citations"
}
}

Understanding Similarity Scores

similarity_score (0.0 - 1.0) indicates cosine similarity between your query and the observation, and confidence_level provides a categorical interpretation:

Score RangeConfidence LevelInterpretation
>= 0.75highStrong semantic match
0.50 - 0.74mediumModerate relevance
< 0.50lowLoosely related

Tip: Use "min_similarity": 0.75 in your search request for high-precision results.

Narrow results using available search filters.

Search Parameters

The POST /v1/483/observations/search endpoint accepts these parameters:

ParameterTypeRequiredDescription
querystringYesNatural language search query (3-1000 chars)
top_kintegerNoNumber of semantic results (default: 10, max: 50)
min_similarityfloatNoMinimum cosine similarity threshold (0-1)
fiscal_yearintegerNoFilter results by fiscal year
program_areastringNoFilter by FDA program area

By Program Area

Search within specific FDA program areas:

response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"query": "cleaning validation procedures",
"top_k": 20,
"program_area": "Human Drugs"
}
)

data = response.json()
print(f"Found {data['total']} results in Human Drugs")

By Fiscal Year

Filter results to a specific fiscal year:

curl -X POST "https://api.ctwise.ai/v1/483/observations/search" \
-H "X-Api-Key: $CTWISE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "aseptic processing contamination",
"top_k": 15,
"fiscal_year": 2025
}'

With Similarity Threshold

Only return high-confidence matches:

response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"query": "equipment qualification",
"top_k": 20,
"min_similarity": 0.75
}
)

data = response.json()
print(f"Found {data['total']} high-confidence results")

Listing Observations with GET

Use GET /v1/483/observations for paginated listing with filters:

response = requests.get(
f"{BASE_URL}/483/observations",
headers={"X-Api-Key": API_KEY},
params={
"fei_number": "3016004437",
"fiscal_year": 2025,
"limit": 20,
"offset": 0
}
)

data = response.json()
print(f"Total observations for facility: {data['total']}")
for obs in data["results"]:
print(f" {obs['act_cfr_number']}: {obs['short_description'][:80]}...")

Available GET filters: fei_number, fiscal_year, program_area, act_cfr_number, limit, offset

Find observations for specific 21 CFR sections.

Search by CFR Section

# Find all 21 CFR 211.84 (Testing and release) observations
response = requests.get(
f"{BASE_URL}/483/observations",
headers={"X-Api-Key": API_KEY},
params={
"act_cfr_number": "21 CFR 211.84",
"limit": 30
}
)

data = response.json()

print(f"\n=== 21 CFR 211.84 OBSERVATIONS ===")
print(f"Total: {data['total']}\n")

# Analyze common themes
for obs in data["results"]:
print(f"- {obs['legal_name']} ({obs['fei_number']})")
print(f" {obs['short_description'][:150]}...")
print()

Common Critical CFRs

# Search multiple critical CFRs in parallel
import concurrent.futures

critical_cfrs = [
"21 CFR 211.84", # Testing and release
"21 CFR 211.180", # General requirements (records)
"21 CFR 211.192", # Production record review
"21 CFR 211.68", # Equipment validation
"21 CFR 211.113", # Microbiological contamination
]

def search_cfr(cfr):
"""Search for a specific CFR section."""
response = requests.get(
f"{BASE_URL}/483/observations",
headers={"X-Api-Key": API_KEY},
params={
"act_cfr_number": cfr,
"fiscal_year": 2025,
"limit": 100
}
)
return cfr, response.json()

# Parallel search
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(search_cfr, cfr) for cfr in critical_cfrs]
results = [f.result() for f in concurrent.futures.as_completed(futures)]

# Rank by frequency
print("\n=== MOST COMMON CRITICAL CFRs (FY 2025) ===\n")
for cfr, data in sorted(results, key=lambda x: x[1]['total'], reverse=True):
print(f"{cfr}: {data['total']} observations")

Output:

=== MOST COMMON CRITICAL CFRs (OAI, 2024-Present) ===

21 CFR 211.180: 342 OAI observations
21 CFR 211.84: 289 OAI observations
21 CFR 211.192: 267 OAI observations
21 CFR 211.68: 213 OAI observations
21 CFR 211.113: 156 OAI observations

Combine observation search with facility lookup to analyze specific manufacturers.

Step 1: Look Up a Facility

If you know the FEI number, look up the facility profile directly:

# Get facility profile
fei = "3016004437"
facility_response = requests.get(
f"{BASE_URL}/483/facilities/{fei}",
headers={"X-Api-Key": API_KEY}
)

facility = facility_response.json()
print(f"Facility: {facility['legal_name']}")
print(f"Location: {facility['city']}, {facility['state']}")
print(f"Total inspections: {facility['total_inspections']}")
print(f"Risk: {facility['risk_assessment']['risk_score']} ({facility['risk_assessment']['risk_level']})")

Or browse facilities by state:

facilities_response = requests.get(
f"{BASE_URL}/483/facilities",
headers={"X-Api-Key": API_KEY},
params={"state": "NJ", "limit": 20}
)
for f in facilities_response.json()["results"]:
print(f"{f['fei_number']}: {f['legal_name']}")

Step 2: Get Citations for That Facility

# Get all citations for this facility
citations_response = requests.get(
f"{BASE_URL}/483/facilities/{fei}/citations",
headers={"X-Api-Key": API_KEY},
params={"limit": 100}
)

data = citations_response.json()
citations = data["results"]

print(f"\n=== FACILITY CITATION HISTORY ===")
print(f"Total citations: {data['total']}\n")

# Group by CFR
from collections import defaultdict
cfr_counts = defaultdict(int)

for obs in citations:
cfr = obs.get("act_cfr_number", "Unspecified")
cfr_counts[cfr] += 1

print("Most common CFRs:")
for cfr, count in sorted(cfr_counts.items(), key=lambda x: x[1], reverse=True)[:5]:
print(f" {cfr}: {count} citations")
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime

# Convert citations to DataFrame
df = pd.DataFrame([
{
"date": datetime.fromisoformat(obs["inspection_end_date"]),
"cfr": obs.get("act_cfr_number", "Unspecified")
}
for obs in citations
])

# Plot citation count by year
yearly_counts = df.groupby(df["date"].dt.year).size()

yearly_counts.plot(kind="bar", figsize=(10, 6), color="#3498db")
plt.title(f"Citation History: {facility['legal_name']}")
plt.xlabel("Year")
plt.ylabel("Citation Count")
plt.tight_layout()
plt.savefig("facility_citation_trends.png")
plt.show()

Advanced Techniques

Enriching Observations with Facility Context

Combine observation search results with facility profiles and risk scores for richer analysis:

# Step 1: Search for observations
response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"query": "change control procedures",
"top_k": 10
}
)

results = response.json()["results"]

# Step 2: Enrich each result with facility context
seen_feis = {}
for obs in results:
fei = obs["fei_number"]

# Cache facility lookups to avoid duplicate calls
if fei not in seen_feis:
fac_response = requests.get(
f"{BASE_URL}/483/facilities/{fei}",
headers={"X-Api-Key": API_KEY}
)
seen_feis[fei] = fac_response.json() if fac_response.status_code == 200 else None

facility = seen_feis[fei]

print(f"\n{'='*60}")
print(f"Observation: {obs['short_description'][:100]}...")
print(f"CFR: {obs['act_cfr_number']}")
if facility:
print(f"\nFacility Context:")
print(f" Name: {facility['legal_name']}")
print(f" Location: {facility['city']}, {facility['state']}")
print(f" Total Inspections: {facility['total_inspections']}")
print(f" Risk: {facility['risk_assessment']['risk_score']} ({facility['risk_assessment']['risk_level']})")

Regulatory Mapping: Cross-Reference with CTWise Rules

Link 483 observations to regulatory requirements using CFR cross-references:

# Step 1: Search for observations
obs_response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={
"X-Api-Key": API_KEY,
"Content-Type": "application/json"
},
json={
"query": "validation master plan",
"top_k": 5
}
)

results = obs_response.json()["results"]

# Step 2: For each observation, look up CFR cross-references
for obs in results:
cfr = obs.get("act_cfr_number", "")

if cfr:
# Get CFR detail with cross-references to CTWise regulatory rules
cfr_response = requests.get(
f"{BASE_URL}/483/cfr-references/{cfr}",
headers={"X-Api-Key": API_KEY},
params={"cfr": cfr}
)

cfr_data = cfr_response.json()

print(f"\n{'='*60}")
print(f"483 Observation: {obs['short_description'][:100]}...")
print(f"CFR: {cfr} ({cfr_data.get('occurrence_count', 'N/A')} total citations)")
print(f"\nCross-Referenced Regulatory Rules:")

cross_refs = cfr_data.get("cross_references", [])
if cross_refs:
for ref in cross_refs:
print(f" - [{ref['source']}] {ref['title']}")
print(f" Confidence: {ref['confidence']:.2f}")
else:
print(" No cross-references found")

Building a Compliance Trend Report

Combine multiple API calls to build a CFR citation frequency report:

#!/usr/bin/env python3
"""
Compliance Trend Report Generator

Uses CTWise 483 Intelligence API to analyze CFR citation frequencies.
"""

import os
import requests
from collections import Counter

API_KEY = os.getenv("CTWISE_API_KEY")
BASE_URL = "https://api.ctwise.ai/v1"

def fetch_top_cfr_references(limit=20):
"""Fetch top CFR references by citation count."""
response = requests.get(
f"{BASE_URL}/483/cfr-references",
headers={"X-Api-Key": API_KEY},
params={"sort_order": "desc", "limit": limit}
)
return response.json()

def fetch_analytics_summary():
"""Fetch aggregate analytics."""
response = requests.get(
f"{BASE_URL}/483/analytics/summary",
headers={"X-Api-Key": API_KEY}
)
return response.json()

def generate_report():
"""Generate compliance trend report."""

# Fetch data
summary = fetch_analytics_summary()
cfr_data = fetch_top_cfr_references(limit=10)

print(f"""
COMPLIANCE TREND REPORT
{'='*60}
Report Date: {os.popen('date +%Y-%m-%d').read().strip()}
{'='*60}

DATASET OVERVIEW
{'='*60}
Total Citations: {summary['total_citations']}
Total Facilities: {summary['total_facilities']}
Total CFR References: {summary['total_cfr_references']}

Risk Distribution:""")

for level, count in summary.get("risk_distribution", {}).items():
print(f" {level}: {count} facilities")

print(f"""
TOP CFR CITATIONS
{'='*60}""")

for i, cfr in enumerate(cfr_data["results"], 1):
print(f"{i:2d}. {cfr['act_cfr_number']}: {cfr['occurrence_count']} citations")
if cfr.get("short_description"):
print(f" {cfr['short_description'][:80]}...")

# Drill into a specific CFR for cross-references
if cfr_data["results"]:
top_cfr = cfr_data["results"][0]["act_cfr_number"]
detail_response = requests.get(
f"{BASE_URL}/483/cfr-references/{top_cfr}",
headers={"X-Api-Key": API_KEY},
params={"cfr": top_cfr}
)
detail = detail_response.json()

print(f"""
DEEP DIVE: {top_cfr}
{'='*60}
Total Citations: {detail.get('occurrence_count', 'N/A')}""")

cross_refs = detail.get("cross_references", [])
if cross_refs:
print("Cross-Referenced Regulatory Rules:")
for ref in cross_refs[:3]:
print(f" - [{ref['source']}] {ref['title']}")

print(f"\n{'='*60}")
print("Report generated by CTWise 483 Intelligence")

if __name__ == "__main__":
generate_report()

Run the script:

python compliance_trend_report.py

Output:

COMPLIANCE TREND REPORT
============================================================
Report Date: 2026-02-21
============================================================

DATASET OVERVIEW
============================================================
Total Citations: 25522
Total Facilities: 8146
Total CFR References: 1410

Risk Distribution:
low: 3421 facilities
medium: 2851 facilities
high: 1467 facilities
critical: 407 facilities

TOP CFR CITATIONS
============================================================
1. 21 CFR 211.180: 342 citations
General requirements for records and reports...
2. 21 CFR 211.84: 289 citations
Testing and approval or rejection of components...
3. 21 CFR 211.192: 267 citations
Production record review...
4. 21 CFR 211.68: 213 citations
Automatic, mechanical, and electronic equipment...
5. 21 CFR 211.160: 198 citations
General requirements for laboratory controls...

DEEP DIVE: 21 CFR 211.180
============================================================
Total Citations: 342
Cross-Referenced Regulatory Rules:
- [FDA] 21 CFR Part 211 - Current Good Manufacturing Practice
- [ICH] ICH Q7 - Good Manufacturing Practice

============================================================
Report generated by CTWise 483 Intelligence

Query Optimization Tips

Natural Language vs. Keyword Queries

CTWise uses semantic search, which understands natural language better than keyword matching.

❌ Less Effective (Keyword Matching):

{"query": "211.84 testing release"}

✅ More Effective (Natural Language):

{"query": "failures in batch testing and release procedures"}

Why? Semantic search understands:

  • Synonyms: "batch testing" = "lot testing" = "product testing"
  • Related concepts: "release procedures" includes "disposition", "approval", "QA review"
  • Context: Understands the relationship between testing and release

Combining Search with Filters

Use available filters to narrow results:

# Too broad
response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={"query": "laboratory", "top_k": 50}
)

# More precise: add descriptive context + filters
response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={
"query": "laboratory controls out-of-specification investigations",
"top_k": 20,
"fiscal_year": 2025,
"program_area": "Human Drugs",
"min_similarity": 0.70
}
)

Similarity Threshold Tuning

Use min_similarity to control precision vs. recall:

def search_with_threshold(query, min_similarity=0.75):
"""Search with minimum similarity threshold."""

response = requests.post(
f"{BASE_URL}/483/observations/search",
headers={"X-Api-Key": API_KEY, "Content-Type": "application/json"},
json={
"query": query,
"top_k": 50,
"min_similarity": min_similarity
}
)

data = response.json()
print(f"Found {data['total']} results above {min_similarity} threshold")

for obs in data["results"][:3]:
print(f" {obs['similarity_score']:.2f} ({obs['confidence_level']}): {obs['short_description'][:80]}...")

return data["results"]

# High precision (fewer, more relevant results)
high_precision = search_with_threshold("data integrity audit trails", min_similarity=0.85)

# High recall (more results, some less relevant)
high_recall = search_with_threshold("data integrity audit trails", min_similarity=0.60)

Recommendation:

  • Exploratory research: Use 0.50-0.65 threshold
  • Regulatory intelligence: Use 0.70-0.80 threshold
  • High precision only: Use 0.80+ threshold

Complete Example: Monthly 483 Intelligence Report

Combine multiple API endpoints to generate a comprehensive monthly report:

#!/usr/bin/env python3
"""
Monthly 483 Intelligence Report

Generates a comprehensive monthly report with:
1. Dataset overview and risk distribution
2. Top cited CFR sections
3. Focus area analysis (e.g., data integrity)
4. Supplier risk score checks
"""

import os
import requests
import pandas as pd

API_KEY = os.getenv("CTWISE_API_KEY")
BASE_URL = "https://api.ctwise.ai/v1"
headers = {"X-Api-Key": API_KEY, "Content-Type": "application/json"}

def get_analytics_summary():
"""Get aggregate analytics across all 483 datasets."""
response = requests.get(
f"{BASE_URL}/483/analytics/summary",
headers={"X-Api-Key": API_KEY}
)
return response.json()

def get_top_cfr_references(limit=10):
"""Get top CFR references by citation count."""
response = requests.get(
f"{BASE_URL}/483/cfr-references",
headers={"X-Api-Key": API_KEY},
params={"sort_order": "desc", "limit": limit}
)
return response.json()

def search_focus_area(query, top_k=10):
"""Semantic search for specific compliance focus area."""
response = requests.post(
f"{BASE_URL}/483/observations/search",
headers=headers,
json={
"query": query,
"top_k": top_k,
"min_similarity": 0.70
}
)
return response.json()

def check_supplier_risk_scores(fei_list):
"""Check risk scores for a list of supplier FEI numbers."""
risk_scores = []
for fei in fei_list:
score_response = requests.get(
f"{BASE_URL}/483/risk-scores/{fei}",
headers={"X-Api-Key": API_KEY}
)

if score_response.status_code == 200:
score = score_response.json()
risk_scores.append({
"fei_number": fei,
"legal_name": score["legal_name"],
"risk_score": score["risk_score"],
"risk_level": score["risk_level"],
"total_inspections": score["context"]["total_inspections"],
"oai_count": score["context"]["oai_count"],
"last_inspection": score["context"]["last_inspection_date"]
})

return risk_scores

def generate_monthly_report(supplier_feis):
"""Generate complete monthly intelligence report."""

print("\n" + "="*70)
print("MONTHLY FDA 483 INTELLIGENCE REPORT")
print("="*70)

# 1. Dataset Overview
print("\n[1] DATASET OVERVIEW\n")
summary = get_analytics_summary()

print(f"Total Citations: {summary['total_citations']}")
print(f"Total Facilities: {summary['total_facilities']}")
print(f"Total CFR References: {summary['total_cfr_references']}")
print(f"\nRisk Distribution:")
for level, count in summary.get("risk_distribution", {}).items():
print(f" {level}: {count} facilities")

# 2. Top CFR Citations
print("\n[2] TOP CFR CITATIONS\n")
cfr_data = get_top_cfr_references(limit=10)

for i, cfr in enumerate(cfr_data["results"], 1):
print(f" {i}. {cfr['act_cfr_number']}: {cfr['occurrence_count']} citations")

# 3. Focus Area: Data Integrity
print("\n[3] FOCUS AREA: DATA INTEGRITY\n")
di_results = search_focus_area("data integrity electronic records audit trails", top_k=5)

print(f"Found {di_results['total']} relevant observations\n")

if di_results["results"]:
print("Top Matches:")
for obs in di_results["results"][:3]:
print(f" - {obs['legal_name']} ({obs['fei_number']})")
print(f" CFR: {obs['act_cfr_number']}")
print(f" {obs['short_description'][:120]}...")
print(f" Similarity: {obs['similarity_score']:.2f} ({obs['confidence_level']})")
print()

# 4. Supplier Risk Scores
if supplier_feis:
print("[4] SUPPLIER RISK SCORES\n")
risk_scores = check_supplier_risk_scores(supplier_feis)

df_risk = pd.DataFrame(risk_scores)
df_risk = df_risk.sort_values("risk_score", ascending=False)

for _, row in df_risk.iterrows():
print(f" {row['legal_name']} (FEI: {row['fei_number']})")
print(f" Risk: {row['risk_score']:.1f}/100 ({row['risk_level']})")
print(f" Inspections: {row['total_inspections']} | OAI: {row['oai_count']}")
print(f" Last Inspection: {row['last_inspection']}")
print()

# High-Risk Alerts
high_risk = df_risk[df_risk["risk_level"].isin(["high", "critical"])]
if not high_risk.empty:
print(f" HIGH RISK ALERTS: {len(high_risk)} suppliers require attention")
for _, row in high_risk.iterrows():
print(f" - {row['legal_name']}: {row['risk_score']:.1f} ({row['risk_level']})")

print("\n" + "="*70)
print("Report completed successfully")
print("="*70 + "\n")

if __name__ == "__main__":
# Your critical supplier FEI numbers
MY_SUPPLIERS = [
"1000234567", # Supplier A
"1000345678", # Supplier B
"1000456789", # Supplier C
]
generate_monthly_report(MY_SUPPLIERS)

Run the script:

python monthly_483_report.py

Next Steps

  1. Start with basic searches to understand observation patterns
  2. Use filters to narrow by fiscal year, program area, or CFR section
  3. Look up facility profiles and risk scores for key suppliers
  4. Build custom reports using the monthly report script as a template
  5. Cross-reference CFR citations with CTWise regulatory rules for full context

For more information: