When you're writing scripts or building applications that call an external API, things will occasionally go wrong. Images might be invalid, credits might run out, the network might hiccup, or rate limits might kick in. Good error handling is the difference between a script that crashes mysteriously and one that tells you exactly what went wrong and recovers gracefully.
The PrintPal client libraries provide specific exception classes for every type of error the API can return. Let's go through each one.
The Error Types
Python:
| from printpal import (
PrintPal,
PrintPalError, # Base error - catches everything
AuthenticationError, # Invalid or missing API key
InsufficientCreditsError,# Not enough credits
ValidationError, # Invalid parameters
GenerationError, # Generation failed
NotFoundError, # Resource not found
RateLimitError, # Rate limit exceeded
TimeoutError, # Request or poll timed out
)
|
JavaScript/TypeScript:
| import {
PrintPal,
PrintPalError, // Base error
AuthenticationError, // Invalid API key
InsufficientCreditsError,// Not enough credits
ValidationError, // Invalid parameters
GenerationError, // Generation failed
NotFoundError, // Resource not found
RateLimitError, // Too many requests
TimeoutError, // Timed out
} from 'printpal';
|
Writing a Robust Generation Function
Here's how to handle errors properly in a real-world scenario:
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 | from printpal import (
PrintPal, Quality, Format,
AuthenticationError,
InsufficientCreditsError,
ValidationError,
RateLimitError,
GenerationError,
TimeoutError,
PrintPalError,
)
import time
def generate_model(image_path, output_path, quality=Quality.DEFAULT):
"""Generate a 3D model with full error handling."""
client = PrintPal()
try:
# Check credits before starting
credits = client.get_credits()
print(f"Credits available: {credits.credits}")
# Generate and download
path = client.generate_and_download(
image_path=image_path,
output_path=output_path,
quality=quality,
)
print(f"Success! Model saved to: {path}")
return path
except AuthenticationError:
print("Error: Invalid API key. Check your PRINTPAL_API_KEY.")
return None
except InsufficientCreditsError as e:
print(f"Error: Not enough credits.")
print(f" Need: {e.credits_required}, Have: {e.credits_available}")
print(f" Purchase more at: https://printpal.io/buy-credits")
return None
except ValidationError as e:
print(f"Error: Invalid parameters - {e.message}")
return None
except RateLimitError as e:
print(f"Rate limited. Waiting {e.retry_after} seconds...")
time.sleep(int(e.retry_after or 60))
# You could retry here
return None
except GenerationError as e:
print(f"Generation failed: {e.message}")
print(f" Generation UID: {e.generation_uid}")
return None
except TimeoutError:
print("Generation timed out. Try again or use a lower quality.")
return None
except PrintPalError as e:
print(f"API error: {e.message}")
return None
|
JavaScript/TypeScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 | import {
PrintPal, Quality, Format,
AuthenticationError,
InsufficientCreditsError,
ValidationError,
RateLimitError,
GenerationError,
TimeoutError,
PrintPalError,
} from 'printpal';
async function generateModel(imagePath, outputPath, quality = Quality.DEFAULT) {
const client = new PrintPal({ apiKey: process.env.PRINTPAL_API_KEY });
try {
const credits = await client.getCredits();
console.log(`Credits available: ${credits.credits}`);
const path = await client.generateAndDownload(
imagePath,
outputPath,
{ quality }
);
console.log(`Success! Model saved to: ${path}`);
return path;
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key. Check your PRINTPAL_API_KEY.');
} else if (error instanceof InsufficientCreditsError) {
console.error(`Not enough credits. Need: ${error.creditsRequired}`);
} else if (error instanceof RateLimitError) {
console.error('Rate limited. Try again later.');
} else if (error instanceof GenerationError) {
console.error(`Generation failed: ${error.message}`);
} else if (error instanceof TimeoutError) {
console.error('Generation timed out.');
} else if (error instanceof PrintPalError) {
console.error(`API error: ${error.message}`);
} else {
throw error;
}
return null;
}
}
|
What error type is raised when your account doesn't have enough credits for a generation?
AuthenticationError
InsufficientCreditsError
ValidationError
GenerationError
Best Practice: Check Credits Before Generating
A simple but valuable pattern is to check your credit balance before submitting a generation. This avoids wasting time on a request that will fail:
Python:
1
2
3
4
5
6
7
8
9
10
11
12 | from printpal import CREDIT_COSTS, Quality
# Know the cost before you spend
quality = Quality.SUPER
cost = CREDIT_COSTS[quality]
credits = client.get_credits()
if credits.credits < cost:
print(f"Not enough credits. Need {cost}, have {credits.credits}.")
else:
# Safe to proceed
result = client.generate_from_image("image.png", quality=quality)
|
Best Practice: Use Environment Variables for Configuration
Keep your API key and other configuration out of your source code:
Python:
| import os
API_KEY = os.environ.get("PRINTPAL_API_KEY")
if not API_KEY:
raise RuntimeError("Please set the PRINTPAL_API_KEY environment variable")
client = PrintPal(api_key=API_KEY)
|
For projects with multiple settings, consider using a .env file with the python-dotenv library (just make sure .env is in your .gitignore).
Best Practice: Use the Context Manager (Python)
The Python client supports context managers, which automatically clean up the HTTP session when you're done:
| from printpal import PrintPal
with PrintPal() as client:
credits = client.get_credits()
print(f"Credits: {credits.credits}")
# Session is automatically closed when the block exits
|
This is especially good practice in scripts that run and exit, or in web applications where you want to avoid connection leaks.
Best Practice: Monitor Your Usage
The API provides a usage endpoint that shows your recent activity:
Python:
| usage = client.get_usage()
print(f"API key: {usage.api_key.name}")
print(f"Total requests: {usage.api_key.total_requests}")
print(f"Credits used: {usage.api_key.credits_used}")
print(f"Credits remaining: {usage.credits_remaining}")
# Show recent requests
for req in usage.recent_requests[:5]:
print(f" {req['endpoint']} - {req['status_code']} - {req['credits_used']} credits")
|
This is useful for debugging, auditing, and keeping track of how your automated processes are consuming credits.
Why is using a context manager (the 'with' statement) recommended for the Python client?
It makes the generation faster
It automatically retries failed requests
It automatically closes the HTTP session when done, preventing connection leaks
It provides better error messages