aiondtech 2.1.0__py3-none-any.whl → 2.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
aiondtech/models.py CHANGED
@@ -1,433 +1,3 @@
1
- # """
2
- # Data models and exceptions for the AiondTech SDK v2.
3
- # """
4
-
5
- # from dataclasses import dataclass, field
6
- # from typing import List, Optional, Dict, Any
7
- # from datetime import datetime
8
-
9
-
10
- # # ============================================================================
11
- # # EXCEPTIONS
12
- # # ============================================================================
13
-
14
- # class APIError(Exception):
15
- # """Base exception for all API errors."""
16
-
17
- # def __init__(
18
- # self,
19
- # message: str,
20
- # status_code: Optional[int] = None,
21
- # response: Optional[Dict[str, Any]] = None
22
- # ):
23
- # self.message = message
24
- # self.status_code = status_code
25
- # self.response = response or {}
26
- # super().__init__(self.message)
27
-
28
- # def __str__(self):
29
- # if self.status_code:
30
- # return f"[{self.status_code}] {self.message}"
31
- # return self.message
32
-
33
-
34
- # class AuthenticationError(APIError):
35
- # """Raised when API key is invalid or missing."""
36
- # pass
37
-
38
-
39
- # class RateLimitError(APIError):
40
- # """Raised when rate limit is exceeded."""
41
-
42
- # def __init__(
43
- # self,
44
- # message: str = "Rate limit exceeded",
45
- # retry_after: Optional[int] = None,
46
- # **kwargs
47
- # ):
48
- # self.retry_after = retry_after
49
- # super().__init__(message, **kwargs)
50
-
51
-
52
- # class ValidationError(APIError):
53
- # """Raised when request validation fails."""
54
- # pass
55
-
56
-
57
- # class NotFoundError(APIError):
58
- # """Raised when a resource is not found."""
59
- # pass
60
-
61
-
62
- # class InsufficientCreditsError(APIError):
63
- # """Raised when user has insufficient API credits."""
64
-
65
- # def __init__(
66
- # self,
67
- # message: str = "Insufficient API credits",
68
- # credits_remaining: int = 0,
69
- # credits_required: int = 0,
70
- # **kwargs
71
- # ):
72
- # self.credits_remaining = credits_remaining
73
- # self.credits_required = credits_required
74
- # super().__init__(message, **kwargs)
75
-
76
-
77
- # # ============================================================================
78
- # # RESPONSE MODELS
79
- # # ============================================================================
80
-
81
- # @dataclass
82
- # class ResumeUploadResult:
83
- # """Result from uploading a resume (upload only, no parsing)."""
84
- # resume_id: int
85
- # message: str
86
- # credits_used: int = 1
87
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
88
-
89
- # @classmethod
90
- # def from_response(cls, data: Dict[str, Any]) -> "ResumeUploadResult":
91
- # return cls(
92
- # resume_id=data.get("resume_id"),
93
- # message=data.get("message", "Resume uploaded successfully"),
94
- # credits_used=data.get("credits_used", 1),
95
- # _raw=data
96
- # )
97
-
98
- # def __repr__(self):
99
- # return f"<ResumeUploadResult resume_id={self.resume_id}>"
100
-
101
-
102
- # @dataclass
103
- # class ResumeAnalysisResult:
104
- # """Result from uploading and analyzing a resume."""
105
- # resume_id: int
106
- # parsed_data: Dict[str, Any]
107
- # credits_used: int = 3
108
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
109
-
110
- # @classmethod
111
- # def from_response(cls, data: Dict[str, Any]) -> "ResumeAnalysisResult":
112
- # return cls(
113
- # resume_id=data.get("resume_id"),
114
- # parsed_data=data.get("parsed_data", {}),
115
- # credits_used=data.get("credits_used", 3),
116
- # _raw=data
117
- # )
118
-
119
- # @property
120
- # def full_name(self) -> Optional[str]:
121
- # return self.parsed_data.get("full_name")
122
-
123
- # @property
124
- # def email(self) -> Optional[str]:
125
- # return self.parsed_data.get("email")
126
-
127
- # @property
128
- # def skills(self) -> List[str]:
129
- # skills = self.parsed_data.get("skills", [])
130
- # if isinstance(skills, str):
131
- # return [s.strip() for s in skills.split(",") if s.strip()]
132
- # return skills or []
133
-
134
- # @property
135
- # def job_titles(self) -> List[str]:
136
- # titles = self.parsed_data.get("job_titles", [])
137
- # if isinstance(titles, str):
138
- # return [t.strip() for t in titles.split(",") if t.strip()]
139
- # return titles or []
140
-
141
- # @property
142
- # def total_experience(self) -> Optional[str]:
143
- # return self.parsed_data.get("total_experience")
144
-
145
- # def __repr__(self):
146
- # name = self.full_name or "Unknown"
147
- # return f"<ResumeAnalysisResult resume_id={self.resume_id} name='{name}'>"
148
-
149
-
150
- # @dataclass
151
- # class ResumeComparisonResult:
152
- # """Result from comparing a resume to a job posting."""
153
- # resume_id: int
154
- # job_id: int
155
- # comparison_score: float
156
- # comparison_reason: str
157
- # parsed_data: Optional[Dict[str, Any]] = None
158
- # matched_keywords: List[str] = field(default_factory=list)
159
- # missing_keywords: List[str] = field(default_factory=list)
160
- # suggestions: List[str] = field(default_factory=list)
161
- # credits_used: int = 3
162
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
163
-
164
- # @classmethod
165
- # def from_response(cls, data: Dict[str, Any]) -> "ResumeComparisonResult":
166
- # return cls(
167
- # resume_id=data.get("resume_id"),
168
- # job_id=data.get("job_id"),
169
- # comparison_score=data.get("comparison_score", 0) or data.get("ats_score", 0),
170
- # comparison_reason=data.get("comparison_reason", "") or data.get("reason", ""),
171
- # parsed_data=data.get("parsed_data"),
172
- # matched_keywords=data.get("matched_keywords", []),
173
- # missing_keywords=data.get("missing_keywords", []),
174
- # suggestions=data.get("suggestions", []),
175
- # credits_used=data.get("credits_used", 3),
176
- # _raw=data
177
- # )
178
-
179
- # @property
180
- # def is_good_match(self) -> bool:
181
- # """Returns True if score is 70% or higher."""
182
- # return self.comparison_score >= 70
183
-
184
- # @property
185
- # def match_level(self) -> str:
186
- # """Returns match level: 'excellent', 'good', 'fair', 'poor'."""
187
- # if self.comparison_score >= 85:
188
- # return "excellent"
189
- # elif self.comparison_score >= 70:
190
- # return "good"
191
- # elif self.comparison_score >= 50:
192
- # return "fair"
193
- # return "poor"
194
-
195
- # def __repr__(self):
196
- # return f"<ResumeComparisonResult resume_id={self.resume_id} score={self.comparison_score}%>"
197
-
198
-
199
- # @dataclass
200
- # class JobResult:
201
- # """Result from creating a job posting."""
202
- # job_id: int
203
- # title: str
204
- # description: str
205
- # created_by: str
206
- # created_at: Optional[str] = None
207
- # credits_used: int = 1
208
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
209
-
210
- # @classmethod
211
- # def from_response(cls, data: Dict[str, Any]) -> "JobResult":
212
- # return cls(
213
- # job_id=data.get("job_id"),
214
- # title=data.get("title", ""),
215
- # description=data.get("description", ""),
216
- # created_by=data.get("created_by", ""),
217
- # created_at=data.get("created_at"),
218
- # credits_used=data.get("credits_used", 1),
219
- # _raw=data
220
- # )
221
-
222
- # def __repr__(self):
223
- # return f"<JobResult job_id={self.job_id} title='{self.title}'>"
224
-
225
-
226
- # @dataclass
227
- # class ParsedResumeResult:
228
- # """Result from parsing/analyzing a resume by ID."""
229
- # resume_id: int
230
- # partner: str
231
- # parsed_data: Dict[str, Any]
232
- # credits_used: int = 2
233
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
234
-
235
- # @classmethod
236
- # def from_response(cls, data: Dict[str, Any]) -> "ParsedResumeResult":
237
- # return cls(
238
- # resume_id=data.get("resume_id"),
239
- # partner=data.get("partner", ""),
240
- # parsed_data=data.get("parsed_data", {}),
241
- # credits_used=data.get("credits_used", 2),
242
- # _raw=data
243
- # )
244
-
245
- # @property
246
- # def full_name(self) -> Optional[str]:
247
- # return self.parsed_data.get("full_name")
248
-
249
- # @property
250
- # def email(self) -> Optional[str]:
251
- # return self.parsed_data.get("email")
252
-
253
- # @property
254
- # def phone(self) -> Optional[str]:
255
- # return self.parsed_data.get("contact_number")
256
-
257
- # @property
258
- # def linkedin(self) -> Optional[str]:
259
- # return self.parsed_data.get("linkedin")
260
-
261
- # @property
262
- # def location(self) -> Optional[str]:
263
- # return self.parsed_data.get("location")
264
-
265
- # @property
266
- # def skills(self) -> List[str]:
267
- # skills = self.parsed_data.get("skills", [])
268
- # if isinstance(skills, str):
269
- # return [s.strip() for s in skills.split(",") if s.strip()]
270
- # return skills or []
271
-
272
- # @property
273
- # def job_titles(self) -> List[str]:
274
- # titles = self.parsed_data.get("job_titles", [])
275
- # if isinstance(titles, str):
276
- # return [t.strip() for t in titles.split(",") if t.strip()]
277
- # return titles or []
278
-
279
- # @property
280
- # def companies(self) -> List[str]:
281
- # companies = self.parsed_data.get("companies", [])
282
- # if isinstance(companies, str):
283
- # return [c.strip() for c in companies.split(",") if c.strip()]
284
- # return companies or []
285
-
286
- # @property
287
- # def education(self) -> List[str]:
288
- # edu = self.parsed_data.get("education", [])
289
- # if isinstance(edu, str):
290
- # return [e.strip() for e in edu.split(",") if e.strip()]
291
- # return edu or []
292
-
293
- # @property
294
- # def total_experience(self) -> Optional[str]:
295
- # return self.parsed_data.get("total_experience")
296
-
297
- # @property
298
- # def certifications(self) -> List[str]:
299
- # certs = self.parsed_data.get("certifications", [])
300
- # if isinstance(certs, str):
301
- # return [c.strip() for c in certs.split(",") if c.strip()]
302
- # return certs or []
303
-
304
- # def __repr__(self):
305
- # name = self.full_name or "Unknown"
306
- # return f"<ParsedResumeResult resume_id={self.resume_id} name='{name}'>"
307
-
308
-
309
- # @dataclass
310
- # class ResumeListItem:
311
- # """Single resume in a list."""
312
- # id: int
313
- # filename: str
314
- # full_name: Optional[str] = None
315
- # email: Optional[str] = None
316
- # uploaded_at: Optional[str] = None
317
- # is_parsed: bool = False
318
-
319
- # @classmethod
320
- # def from_dict(cls, data: Dict[str, Any]) -> "ResumeListItem":
321
- # return cls(
322
- # id=data.get("id"),
323
- # filename=data.get("filename", ""),
324
- # full_name=data.get("full_name"),
325
- # email=data.get("email"),
326
- # uploaded_at=data.get("uploaded_at"),
327
- # is_parsed=data.get("is_parsed", False)
328
- # )
329
-
330
-
331
- # @dataclass
332
- # class ResumeListResult:
333
- # """Result from listing resumes."""
334
- # resumes: List[Dict[str, Any]]
335
- # total: int
336
- # page: int
337
- # limit: int
338
- # has_more: bool
339
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
340
-
341
- # @classmethod
342
- # def from_response(cls, data: Dict[str, Any]) -> "ResumeListResult":
343
- # return cls(
344
- # resumes=data.get("resumes", []),
345
- # total=data.get("total", 0),
346
- # page=data.get("page", 1),
347
- # limit=data.get("limit", 50),
348
- # has_more=data.get("has_more", False),
349
- # _raw=data
350
- # )
351
-
352
- # def __len__(self):
353
- # return len(self.resumes)
354
-
355
- # def __iter__(self):
356
- # return iter(self.resumes)
357
-
358
- # def __repr__(self):
359
- # return f"<ResumeListResult count={len(self.resumes)} total={self.total}>"
360
-
361
-
362
- # @dataclass
363
- # class JobListItem:
364
- # """Single job in a list."""
365
- # id: int
366
- # title: str
367
- # description: str
368
- # created_at: Optional[str] = None
369
-
370
- # @classmethod
371
- # def from_dict(cls, data: Dict[str, Any]) -> "JobListItem":
372
- # return cls(
373
- # id=data.get("id"),
374
- # title=data.get("title", ""),
375
- # description=data.get("description", ""),
376
- # created_at=data.get("created_at")
377
- # )
378
-
379
-
380
- # @dataclass
381
- # class JobListResult:
382
- # """Result from listing jobs."""
383
- # jobs: List[Dict[str, Any]]
384
- # total: int
385
- # page: int
386
- # limit: int
387
- # has_more: bool
388
- # _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
389
-
390
- # @classmethod
391
- # def from_response(cls, data: Dict[str, Any]) -> "JobListResult":
392
- # return cls(
393
- # jobs=data.get("jobs", []),
394
- # total=data.get("total", 0),
395
- # page=data.get("page", 1),
396
- # limit=data.get("limit", 50),
397
- # has_more=data.get("has_more", False),
398
- # _raw=data
399
- # )
400
-
401
- # def __len__(self):
402
- # return len(self.jobs)
403
-
404
- # def __iter__(self):
405
- # return iter(self.jobs)
406
-
407
- # def __repr__(self):
408
- # return f"<JobListResult count={len(self.jobs)} total={self.total}>"
409
-
410
-
411
- # @dataclass
412
- # class CreditBalance:
413
- # """Credit balance information."""
414
- # total: int
415
- # used_mtd: int
416
- # remaining: int
417
- # plan_name: str
418
- # resets_at: Optional[str] = None
419
-
420
- # @classmethod
421
- # def from_response(cls, data: Dict[str, Any]) -> "CreditBalance":
422
- # return cls(
423
- # total=data.get("total", 0),
424
- # used_mtd=data.get("used_mtd", 0),
425
- # remaining=data.get("remaining", 0),
426
- # plan_name=data.get("plan_name", ""),
427
- # resets_at=data.get("resets_at")
428
- # )
429
-
430
-
431
1
  """
432
2
  Data models and exceptions for the AiondTech SDK v2.
433
3
  """
@@ -513,7 +83,7 @@ class ResumeUploadResult:
513
83
  """Result from uploading a resume (upload only, no parsing)."""
514
84
  resume_id: int
515
85
  message: str
516
- credits_used: int = 1
86
+ credits_used: int = 0
517
87
  _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
518
88
 
519
89
  @classmethod
@@ -521,7 +91,7 @@ class ResumeUploadResult:
521
91
  return cls(
522
92
  resume_id=data.get("resume_id"),
523
93
  message=data.get("message", "Resume uploaded successfully"),
524
- credits_used=data.get("credits_used", 1),
94
+ credits_used=data.get("credits_used", 0),
525
95
  _raw=data
526
96
  )
527
97
 
@@ -534,7 +104,7 @@ class ResumeAnalysisResult:
534
104
  """Result from uploading and analyzing a resume."""
535
105
  resume_id: int
536
106
  parsed_data: Dict[str, Any]
537
- credits_used: int = 3
107
+ credits_used: int = 0
538
108
  _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
539
109
 
540
110
  @classmethod
@@ -542,7 +112,7 @@ class ResumeAnalysisResult:
542
112
  return cls(
543
113
  resume_id=data.get("resume_id"),
544
114
  parsed_data=data.get("parsed_data", {}),
545
- credits_used=data.get("credits_used", 3),
115
+ credits_used=data.get("credits_used", 0),
546
116
  _raw=data
547
117
  )
548
118
 
@@ -589,19 +159,22 @@ class ResumeComparisonResult:
589
159
  matched_keywords: List[str] = field(default_factory=list)
590
160
  missing_keywords: List[str] = field(default_factory=list)
591
161
  suggestions: List[str] = field(default_factory=list)
592
- credits_used: int = 3
162
+ credits_used: int = 0
593
163
  _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
594
164
 
595
165
  @classmethod
596
166
  def from_response(cls, data: Dict[str, Any]) -> "ResumeComparisonResult":
597
167
  # Score field: API returns "score", fallback to legacy field names
598
- score = (
599
- data.get("score", 0)
600
- or data.get("comparison_score", 0)
601
- or data.get("ats_score", 0)
602
- )
168
+ score = data.get("score")
169
+ if score is None:
170
+ score = data.get("comparison_score")
171
+ if score is None:
172
+ score = data.get("ats_score", 0)
173
+
603
174
  # Reason field: API returns "reason", fallback to legacy field name
604
- reason = data.get("reason", "") or data.get("comparison_reason", "")
175
+ reason = data.get("reason")
176
+ if reason is None:
177
+ reason = data.get("comparison_reason", "")
605
178
 
606
179
  return cls(
607
180
  resume_id=data.get("resume_id"),
@@ -613,7 +186,7 @@ class ResumeComparisonResult:
613
186
  matched_keywords=data.get("matched_keywords", []),
614
187
  missing_keywords=data.get("missing_keywords", []),
615
188
  suggestions=data.get("suggestions", []),
616
- credits_used=data.get("credits_used", 3),
189
+ credits_used=data.get("credits_used", 0),
617
190
  _raw=data
618
191
  )
619
192
 
@@ -629,7 +202,7 @@ class JobResult:
629
202
  description: str
630
203
  created_by: str
631
204
  created_at: Optional[str] = None
632
- credits_used: int = 1
205
+ credits_used: int = 0
633
206
  _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
634
207
 
635
208
  @classmethod
@@ -640,7 +213,7 @@ class JobResult:
640
213
  description=data.get("description", ""),
641
214
  created_by=data.get("created_by", ""),
642
215
  created_at=data.get("created_at"),
643
- credits_used=data.get("credits_used", 1),
216
+ credits_used=data.get("credits_used", 0),
644
217
  _raw=data
645
218
  )
646
219
 
@@ -654,7 +227,7 @@ class ParsedResumeResult:
654
227
  resume_id: int
655
228
  partner: str
656
229
  parsed_data: Dict[str, Any]
657
- credits_used: int = 2
230
+ credits_used: int = 0
658
231
  _raw: Dict[str, Any] = field(default_factory=dict, repr=False)
659
232
 
660
233
  @classmethod
@@ -663,7 +236,7 @@ class ParsedResumeResult:
663
236
  resume_id=data.get("resume_id"),
664
237
  partner=data.get("partner", ""),
665
238
  parsed_data=data.get("parsed_data", {}),
666
- credits_used=data.get("credits_used", 2),
239
+ credits_used=data.get("credits_used", 0),
667
240
  _raw=data
668
241
  )
669
242
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiondtech
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Official Python SDK for AiondTech Resume Analyser API
5
5
  Author-email: AiondTech <support@aiondtech.com>
6
6
  Maintainer-email: AiondTech <support@aiondtech.com>
@@ -77,19 +77,31 @@ client = ResumeAnalyser(api_key="your-api-key", production=True)
77
77
 
78
78
  ## API Endpoints
79
79
 
80
+ Credit usage varies depending on resume size and complexity. Every response includes the actual `credits_used` — always check it for accurate tracking.
81
+
80
82
  | Method | Credits | Description |
81
83
  |--------|---------|-------------|
82
- | `resumes.upload()` | 1 | Upload PDF resume |
83
- | `resumes.upload_and_analyze()` | 3 | Upload + AI parsing |
84
- | `resumes.upload_analyze_compare()` | 6 | Upload + parse + compare to job |
85
- | `resumes.analyze()` | 2 | Parse existing resume by ID |
86
- | `resumes.compare_resumes()` | 3 | Compare resume to job |
84
+ | `resumes.upload()` | Varies | Upload PDF resume |
85
+ | `resumes.upload_and_analyze()` | Varies | Upload + AI parsing |
86
+ | `resumes.upload_analyze_compare()` | Varies | Upload + parse + compare to job |
87
+ | `resumes.analyze()` | Varies | Parse existing resume by ID |
88
+ | `resumes.compare_resumes()` | Varies | Compare resume to job |
87
89
  | `resumes.list()` | 0 | List all resumes |
88
- | `jobs.create()` | 1 | Create job posting |
90
+ | `jobs.create()` | Varies | Create job posting |
89
91
  | `jobs.list()` | 0 | List all jobs |
90
92
  | `credits.balance()` | 0 | Check credit balance |
91
93
  | `credits.usage()` | 0 | View usage history |
92
94
 
95
+ **Tracking credits:**
96
+
97
+ ```python
98
+ result = client.resumes.upload_and_analyze("resume.pdf")
99
+ print(f"This call used {result.credits_used} credits")
100
+
101
+ balance = client.credits.balance()
102
+ print(f"Remaining: {balance['remaining']}")
103
+ ```
104
+
93
105
  ---
94
106
 
95
107
  ## Detailed Usage
@@ -107,7 +119,7 @@ result = client.resumes.upload("path/to/resume.pdf")
107
119
  ```python
108
120
  result.resume_id # int — Unique resume ID
109
121
  result.message # str — "Resume uploaded successfully"
110
- result.credits_used # int — 1
122
+ result.credits_used # int — Credits consumed by this call
111
123
  result._raw # dict — Full raw API response
112
124
  ```
113
125
 
@@ -126,7 +138,7 @@ result = client.resumes.upload_and_analyze("path/to/resume.pdf")
126
138
  ```python
127
139
  result.resume_id # int — Unique resume ID
128
140
  result.parsed_data # dict — Full parsed data (see structure below)
129
- result.credits_used # int — 3
141
+ result.credits_used # int — Credits consumed by this call
130
142
  result._raw # dict — Full raw API response
131
143
 
132
144
  # Convenience properties
@@ -175,7 +187,7 @@ result.comparison_score # float — Match score (0-100)
175
187
  result.comparison_reason # str — Detailed reasoning
176
188
  result.language # str | None — Detected language (e.g. "en", "ar")
177
189
  result.parsed_data # dict | None — Parsed resume data
178
- result.credits_used # int — 6
190
+ result.credits_used # int — Credits consumed by this call
179
191
  result._raw # dict — Full raw API response
180
192
  ```
181
193
 
@@ -195,7 +207,7 @@ result = client.resumes.analyze(resume_id=123)
195
207
  result.resume_id # int — Resume ID
196
208
  result.partner # str — Partner identifier
197
209
  result.parsed_data # dict — Full parsed data
198
- result.credits_used # int — 2
210
+ result.credits_used # int — Credits consumed by this call
199
211
  result._raw # dict — Full raw API response
200
212
 
201
213
  # Convenience properties
@@ -233,7 +245,7 @@ job.title # str — Job title
233
245
  job.description # str — Job description
234
246
  job.created_by # str — Creator identifier
235
247
  job.created_at # str | None — Creation timestamp
236
- job.credits_used # int — 1
248
+ job.credits_used # int — Credits consumed by this call
237
249
  job._raw # dict — Full raw API response
238
250
  ```
239
251
 
@@ -254,8 +266,7 @@ result.resume_id # int — Resume ID
254
266
  result.job_id # int — Job ID
255
267
  result.comparison_score # float — Match score (0-100)
256
268
  result.comparison_reason # str — Detailed reasoning
257
- result.language # str | None Detected language (e.g. "en", "ar")
258
- result.credits_used # int — 3
269
+ result.credits_used # intCredits consumed by this call
259
270
  result._raw # dict — Full raw API response
260
271
  ```
261
272
 
@@ -391,12 +402,14 @@ job = client.jobs.create(
391
402
  description="5+ years Python, Django, AWS, PostgreSQL required..."
392
403
  )
393
404
  print(f"Created job #{job.job_id}: {job.title}")
405
+ print(f"Credits used: {job.credits_used}")
394
406
 
395
407
  # Step 2: Upload and analyze a resume
396
408
  analysis = client.resumes.upload_and_analyze("candidate_resume.pdf")
397
409
  print(f"Candidate: {analysis.full_name}")
398
410
  print(f"Skills: {', '.join(analysis.skills)}")
399
411
  print(f"Experience: {analysis.total_experience}")
412
+ print(f"Credits used: {analysis.credits_used}")
400
413
 
401
414
  # Step 3: Compare resume to job
402
415
  comparison = client.resumes.compare_resumes(
@@ -406,6 +419,7 @@ comparison = client.resumes.compare_resumes(
406
419
  print(f"Score: {comparison.comparison_score}%")
407
420
  print(f"Language: {comparison.language}")
408
421
  print(f"Reasoning: {comparison.comparison_reason}")
422
+ print(f"Credits used: {comparison.credits_used}")
409
423
 
410
424
  # Step 4: Check remaining credits
411
425
  balance = client.credits.balance()
@@ -417,6 +431,7 @@ print(f"Credits remaining: {balance['remaining']}")
417
431
  ```python
418
432
  result = client.resumes.upload_analyze_compare("resume.pdf", job_id=job.job_id)
419
433
  print(f"Score: {result.comparison_score}% — {result.comparison_reason}")
434
+ print(f"Credits used: {result.credits_used}")
420
435
  ```
421
436
 
422
437
  ### Batch Processing
@@ -426,18 +441,22 @@ import os
426
441
 
427
442
  job = client.jobs.create("Data Scientist", "ML, Python, statistics...")
428
443
 
444
+ total_credits = job.credits_used
429
445
  results = []
446
+
430
447
  for pdf in os.listdir("resumes/"):
431
448
  if pdf.endswith(".pdf"):
432
449
  r = client.resumes.upload_analyze_compare(
433
450
  f"resumes/{pdf}", job_id=job.job_id
434
451
  )
435
452
  results.append(r)
436
- print(f"{r.comparison_score:5.1f}% — {pdf}")
453
+ total_credits += r.credits_used
454
+ print(f"{r.comparison_score:5.1f}% — {pdf} ({r.credits_used} credits)")
437
455
 
438
456
  # Sort by score
439
457
  results.sort(key=lambda x: x.comparison_score, reverse=True)
440
458
  print(f"\nTop candidate: resume_id={results[0].resume_id} ({results[0].comparison_score}%)")
459
+ print(f"Total credits used: {total_credits}")
441
460
  ```
442
461
 
443
462
  ---