1"""Example usage of pyUSPTO for patent application search and retrieval.
2
3Demonstrates the PatentDataClient for searching applications, retrieving metadata,
4downloading documents, and exporting CSV.
5"""
6
7import json
8import os
9
10from pyUSPTO import ApplicationContinuityData, PatentDataClient, USPTOConfig
11
12DEST_PATH = "./notes/download-example"
13
14# --- Client Initialization ---
15api_key = os.environ.get("USPTO_API_KEY", "YOUR_API_KEY_HERE")
16if api_key == "YOUR_API_KEY_HERE":
17 raise ValueError(
18 "API key is not set. Set the USPTO_API_KEY environment variable."
19 )
20config = USPTOConfig(api_key=api_key)
21client = PatentDataClient(config=config)
22
23print("-" * 40)
24print("Example 1: Default search")
25print("-" * 40)
26
27response = client.search_applications(limit=5)
28print(
29 f"Found {response.count} total patent applications matching default/broad criteria."
30)
31print(
32 f"Displaying first {len(response.patent_file_wrapper_data_bag)} applications from response:"
33)
34
35for patent_wrapper in response.patent_file_wrapper_data_bag:
36 app_meta = patent_wrapper.application_meta_data
37 if app_meta:
38 print(f"\n Application: {patent_wrapper.application_number_text}")
39 print(f" Title: {app_meta.invention_title}")
40 print(f" Status: {app_meta.application_status_description_text}")
41 print(f" Filing Date: {app_meta.filing_date}")
42
43 if app_meta.patent_number:
44 print(f" Patent Number: {app_meta.patent_number}")
45 print(f" Grant Date: {app_meta.grant_date}")
46
47 if app_meta.inventor_bag:
48 print(" Inventors:")
49 for inventor in app_meta.inventor_bag:
50 name_parts = [
51 part
52 for part in [inventor.first_name, inventor.last_name]
53 if part
54 ]
55 print(f" - {' '.join(name_parts).strip()}")
56 if inventor.correspondence_address_bag:
57 address = inventor.correspondence_address_bag[0]
58 if address.city_name and address.geographic_region_code:
59 print(
60 f" ({address.city_name}, {address.geographic_region_code})"
61 )
62
63 if app_meta.applicant_bag:
64 print(" Applicants:")
65 for applicant in app_meta.applicant_bag:
66 print(f" - {applicant.applicant_name_text}")
67
68# Export results to CSV
69if response.count > 0:
70 print("\nGenerating CSV for the current response:")
71 csv_data = response.to_csv()
72 csv_path = os.path.join(DEST_PATH, "patent_search_results.csv")
73 os.makedirs(DEST_PATH, exist_ok=True)
74 with open(csv_path, "w", newline="", encoding="utf-8") as f:
75 f.write(csv_data)
76 print(f"Full CSV data saved to {csv_path}.")
77
78print("-" * 40)
79print("Example 2: Search by inventor name")
80print("-" * 40)
81
82inventor_search_response = client.search_applications(
83 inventor_name_q="Smith", limit=2
84)
85print(
86 f"Found {inventor_search_response.count} patents with 'Smith' as inventor (showing up to 2)."
87)
88for patent_wrapper in inventor_search_response.patent_file_wrapper_data_bag:
89 if patent_wrapper.application_meta_data:
90 print(
91 f" - App No: {patent_wrapper.application_number_text}, Title: {patent_wrapper.application_meta_data.invention_title}"
92 )
93
94print("-" * 40)
95print("Example 3: Search by filing date range")
96print("-" * 40)
97
98date_search_response = client.search_applications(
99 filing_date_from_q="2020-01-01", filing_date_to_q="2020-12-31", limit=2
100)
101print(
102 f"Found {date_search_response.count} patents filed in 2020 (showing up to 2)."
103)
104for patent_wrapper in date_search_response.patent_file_wrapper_data_bag:
105 if patent_wrapper.application_meta_data:
106 print(
107 f" - App No: {patent_wrapper.application_number_text}, Filing Date: {patent_wrapper.application_meta_data.filing_date}"
108 )
109
110print("-" * 40)
111print("Example 4: Get application by number")
112print("-" * 40)
113
114app_no_to_fetch = "18045436"
115print(f"Retrieving patent application: {app_no_to_fetch}")
116patent_wrapper_detail = client.get_application_by_number(
117 application_number=app_no_to_fetch
118)
119if patent_wrapper_detail:
120 print(
121 f"Successfully retrieved: {patent_wrapper_detail.application_number_text}"
122 )
123 if patent_wrapper_detail.application_meta_data:
124 print(
125 f"Title: {patent_wrapper_detail.application_meta_data.invention_title}"
126 )
127
128 print("\nRetrieving document information...")
129 documents_bag = client.get_application_documents(
130 application_number=app_no_to_fetch
131 )
132 print(f"Found {len(documents_bag)} documents for application {app_no_to_fetch}")
133
134 if documents_bag.documents:
135 document_to_download = documents_bag.documents[0]
136 print("\nFirst document details:")
137 print(f" Document ID: {document_to_download.document_identifier}")
138 print(
139 f" Document Type: {document_to_download.document_code} - {document_to_download.document_code_description_text}"
140 )
141 print(f" Date: {document_to_download.official_date}")
142 print(f" Direction: {document_to_download.direction_category}")
143
144 if (
145 document_to_download.document_formats
146 and document_to_download.document_identifier
147 ):
148 print("\nDownloading first PDF document...")
149 print(json.dumps(document_to_download.to_dict(), indent=2))
150 downloaded_path = client.download_document(
151 document=document_to_download,
152 format="PDF",
153 destination=DEST_PATH,
154 overwrite=True,
155 )
156 print(f"Downloaded document to: {downloaded_path}")
157
158 print("\nStreaming same document to memory...")
159 with client.stream_document(
160 document=document_to_download, format="PDF"
161 ) as response:
162 content = response.content
163 print(f"Streamed {len(content)} bytes")
164 else:
165 print(
166 "No downloadable formats available for the first document or document identifier missing."
167 )
168 else:
169 print("No documents listed for this application.")
170
171 # Download publication XML (grant or pgpub)
172 print("\nChecking for publication files (grant/pgpub XML)...")
173 if patent_wrapper_detail.grant_document_meta_data:
174 grant_metadata = patent_wrapper_detail.grant_document_meta_data
175 print(f"Grant document available: {grant_metadata.xml_file_name}")
176 print(f" Product: {grant_metadata.product_identifier}")
177 print(f" Created: {grant_metadata.file_create_date_time}")
178
179 print("\nDownloading grant XML...")
180 grant_path = client.download_publication(
181 printed_metadata=grant_metadata,
182 destination=DEST_PATH,
183 overwrite=True,
184 )
185 print(f"Downloaded grant XML to: {grant_path}")
186
187 if patent_wrapper_detail.pgpub_document_meta_data:
188 pgpub_metadata = patent_wrapper_detail.pgpub_document_meta_data
189 print(f"\nPre-grant publication available: {pgpub_metadata.xml_file_name}")
190
191 pgpub_path = client.download_publication(
192 printed_metadata=pgpub_metadata,
193 file_name="my_pgpub.xml",
194 destination=DEST_PATH,
195 overwrite=True,
196 )
197 print(f"Downloaded pgpub XML to: {pgpub_path}")
198
199 if patent_wrapper_detail.assignment_bag:
200 print("\nAssignments:")
201 for assignment in patent_wrapper_detail.assignment_bag:
202 for assignee in assignment.assignee_bag:
203 print(
204 f" - {assignee.assignee_name_text} (Recorded: {assignment.assignment_recorded_date})"
205 )
206 print(f" Conveyance: {assignment.conveyance_text}")
207else:
208 print(f"Could not retrieve details for application {app_no_to_fetch}")
209
210print("-" * 40)
211print("Example 5: Search by patent number")
212print("-" * 40)
213
214target_patent_number = "10000000"
215print(f"Searching for patent US {target_patent_number} B2...")
216patent_search_response = client.search_applications(
217 patent_number_q=target_patent_number, limit=1
218)
219
220if (
221 patent_search_response.count > 0
222 and patent_search_response.patent_file_wrapper_data_bag
223):
224 found_patent_wrapper = patent_search_response.patent_file_wrapper_data_bag[0]
225 if (
226 found_patent_wrapper.application_meta_data
227 and found_patent_wrapper.application_meta_data.patent_number
228 ):
229 print(
230 f"Retrieved patent: US {found_patent_wrapper.application_meta_data.patent_number}"
231 )
232 else:
233 print(
234 f"Retrieved patent application: {found_patent_wrapper.application_number_text}"
235 )
236
237 if found_patent_wrapper.patent_term_adjustment_data:
238 pta = found_patent_wrapper.patent_term_adjustment_data
239 print(f"Patent Term Adjustment: {pta.adjustment_total_quantity} days")
240 if pta.a_delay_quantity is not None:
241 print(f" A Delay: {pta.a_delay_quantity} days")
242 if pta.b_delay_quantity is not None:
243 print(f" B Delay: {pta.b_delay_quantity} days")
244 if pta.c_delay_quantity is not None:
245 print(f" C Delay: {pta.c_delay_quantity} days")
246 if pta.applicant_day_delay_quantity is not None:
247 print(f" Applicant Delay: {pta.applicant_day_delay_quantity} days")
248
249 continuity_data = ApplicationContinuityData.from_wrapper(
250 wrapper=found_patent_wrapper
251 )
252 if continuity_data.parent_continuity_bag:
253 print("\nParent Applications:")
254 for p_continuity in continuity_data.parent_continuity_bag:
255 print(f" - App No: {p_continuity.parent_application_number_text}")
256 print(
257 f" Type: {p_continuity.claim_parentage_type_code_description_text}"
258 )
259 print(f" Filing Date: {p_continuity.parent_application_filing_date}")
260
261 if continuity_data.child_continuity_bag:
262 print("\nChild Applications:")
263 for c_continuity in continuity_data.child_continuity_bag:
264 print(f" - App No: {c_continuity.child_application_number_text}")
265 print(
266 f" Type: {c_continuity.claim_parentage_type_code_description_text}"
267 )
268 print(f" Filing Date: {c_continuity.child_application_filing_date}")
269else:
270 print(f"No patents found with patent number: {target_patent_number}")
271
272print("-" * 40)
273print("Example 6: POST search")
274print("-" * 40)
275
276print("POST search for applications with 'AI' in title...")
277post_search_body = {
278 "q": "applicationMetaData.inventionTitle:AI",
279 "pagination": {"offset": 0, "limit": 2},
280}
281post_response = client.search_applications(post_body=post_search_body)
282print(
283 f"Found {post_response.count} applications via POST search (showing up to 2)."
284)
285for patent_wrapper in post_response.patent_file_wrapper_data_bag:
286 if patent_wrapper.application_meta_data:
287 print(
288 f" - App No: {patent_wrapper.application_number_text}, Title: {patent_wrapper.application_meta_data.invention_title}"
289 )
290
291print("-" * 40)
292print("Example 7: Search by CPC classification")
293print("-" * 40)
294
295# CPC codes containing spaces or slashes are automatically quoted for the Lucene query.
296print("Searching by CPC classification code 'H10D 64/667'...")
297cpc_response = client.search_applications(classification_q="H10D 64/667", limit=3)
298print(f"Found {cpc_response.count} applications with CPC code H10D 64/667.")
299for patent_wrapper in cpc_response.patent_file_wrapper_data_bag:
300 app_meta = patent_wrapper.application_meta_data
301 if app_meta:
302 print(
303 f" - App No: {patent_wrapper.application_number_text}, Title: {app_meta.invention_title}"
304 )
305 if app_meta.cpc_classification_bag:
306 print(f" CPC codes: {', '.join(app_meta.cpc_classification_bag)}")
307
308print("-" * 40)
309print("Example 8: Get status codes")
310print("-" * 40)
311
312status_code_response = client.get_status_codes(params={"limit": 5})
313print(
314 f"Retrieved {len(status_code_response.status_code_bag)} status codes (out of {status_code_response.count} total)."
315)
316for code_obj in status_code_response.status_code_bag:
317 print(f" - Code: {code_obj.code}, Description: {code_obj.description}")