@@ -389,6 +389,274 @@ total_successful = sum(len(r.successful) for r in all_results)
389389total_failed = sum (len (r.failed) for r in all_results)
390390```
391391
392+ ## Async Bulk Operations
393+
394+ The Bloomy SDK provides async versions of all bulk operations, enabling concurrent execution for significantly better performance when processing multiple items.
395+
396+ ### Benefits of Async Bulk Operations
397+
398+ - ** Concurrent Execution** : Process multiple items simultaneously instead of sequentially
399+ - ** Better Performance** : Reduce total execution time by up to 80% for large batches
400+ - ** Rate Limit Control** : Use ` max_concurrent ` parameter to control request parallelism
401+ - ** Same Error Handling** : Uses the same ` BulkCreateResult ` structure as sync operations
402+
403+ ### Async Todos Example
404+
405+ ``` python
406+ import asyncio
407+ from bloomy import AsyncClient
408+ from datetime import datetime, timedelta
409+
410+ async def create_todos_async ():
411+ async with AsyncClient(api_key = " your-api-key" ) as client:
412+ # Prepare todos
413+ todos = [
414+ {
415+ " meeting_id" : 123 ,
416+ " title" : f " Task { i} " ,
417+ " due_date" : (datetime.now() + timedelta(days = i)).date().isoformat()
418+ }
419+ for i in range (1 , 21 ) # Create 20 todos
420+ ]
421+
422+ # Create with controlled concurrency
423+ result = await client.todo.create_many(todos, max_concurrent = 10 )
424+
425+ print (f " Created { len (result.successful)} todos concurrently " )
426+ if result.failed:
427+ print (f " Failed: { len (result.failed)} " )
428+
429+ # Run the async function
430+ asyncio.run(create_todos_async())
431+ ```
432+
433+ ### Async Issues Example
434+
435+ ``` python
436+ async def bulk_create_issues ():
437+ async with AsyncClient(api_key = " your-api-key" ) as client:
438+ issues = [
439+ {
440+ " meeting_id" : 123 ,
441+ " title" : f " Issue { i} : Performance concern " ,
442+ " notes" : f " Details about issue { i} "
443+ }
444+ for i in range (1 , 11 )
445+ ]
446+
447+ # Higher concurrency for smaller payloads
448+ result = await client.issue.create_many(issues, max_concurrent = 15 )
449+
450+ # Process results
451+ for issue in result.successful:
452+ print (f " Created issue: { issue.title} (ID: { issue.id} ) " )
453+ ```
454+
455+ ### Async Goals Example
456+
457+ ``` python
458+ async def create_quarterly_goals ():
459+ async with AsyncClient(api_key = " your-api-key" ) as client:
460+ # Different goals for team members
461+ team_goals = []
462+ for user_id in [456 , 789 , 101 , 112 ]:
463+ team_goals.extend([
464+ {
465+ " meeting_id" : 123 ,
466+ " title" : f " Q1 Goal for User { user_id} " ,
467+ " user_id" : user_id
468+ }
469+ ])
470+
471+ # Conservative concurrency for complex operations
472+ result = await client.goal.create_many(team_goals, max_concurrent = 5 )
473+
474+ return result
475+ ```
476+
477+ ### Async Meetings Example
478+
479+ ``` python
480+ async def setup_recurring_meetings ():
481+ async with AsyncClient(api_key = " your-api-key" ) as client:
482+ # Create a month of weekly meetings
483+ meetings = [
484+ {
485+ " title" : f " Week { week} Team Sync " ,
486+ " attendees" : [456 , 789 , 321 ]
487+ }
488+ for week in range (1 , 5 )
489+ ]
490+
491+ # Create meetings concurrently
492+ create_result = await client.meeting.create_many(meetings, max_concurrent = 5 )
493+
494+ # Then fetch all details concurrently
495+ meeting_ids = [m[' id' ] for m in create_result.successful]
496+ details_result = await client.meeting.get_many(meeting_ids, max_concurrent = 10 )
497+
498+ return details_result.successful
499+ ```
500+
501+ ### Controlling Concurrency with max_concurrent
502+
503+ The ` max_concurrent ` parameter controls how many requests can be in flight simultaneously:
504+
505+ ``` python
506+ async def demonstrate_concurrency_control ():
507+ async with AsyncClient(api_key = " your-api-key" ) as client:
508+ todos = [{" title" : f " Todo { i} " , " meeting_id" : 123 } for i in range (100 )]
509+
510+ # Conservative: 3 concurrent requests (slower but safer)
511+ result_conservative = await client.todo.create_many(todos, max_concurrent = 3 )
512+
513+ # Moderate: 10 concurrent requests (balanced)
514+ result_moderate = await client.todo.create_many(todos, max_concurrent = 10 )
515+
516+ # Aggressive: 20 concurrent requests (faster but may hit rate limits)
517+ result_aggressive = await client.todo.create_many(todos, max_concurrent = 20 )
518+ ```
519+
520+ !!! tip "Choosing max_concurrent"
521+ - ** Small payloads** (todos, issues): 10-20 concurrent requests
522+ - ** Complex operations** (meetings with attendees): 5-10 concurrent requests
523+ - ** Rate-limited environments** : 3-5 concurrent requests
524+ - ** Default value** : 5 (conservative and safe)
525+
526+ ### Error Handling with Async Bulk Operations
527+
528+ Error handling works the same as sync operations but with async/await syntax:
529+
530+ ``` python
531+ async def robust_bulk_create ():
532+ async with AsyncClient(api_key = " your-api-key" ) as client:
533+ todos = [
534+ {" title" : " Valid todo" , " meeting_id" : 123 },
535+ {" title" : " Missing meeting_id" }, # Will fail
536+ {" title" : " Another valid todo" , " meeting_id" : 123 }
537+ ]
538+
539+ result = await client.todo.create_many(todos)
540+
541+ # Handle successes
542+ successful_ids = [todo.id for todo in result.successful]
543+ print (f " Successfully created todos: { successful_ids} " )
544+
545+ # Handle failures
546+ for failure in result.failed:
547+ print (f " Failed at index { failure.index} : { failure.error} " )
548+ print (f " Failed data: { failure.input_data} " )
549+
550+ # Optionally retry with corrected data
551+ if " meeting_id" in failure.error:
552+ corrected = {** failure.input_data, " meeting_id" : 123 }
553+ retry_result = await client.todo.create(** corrected)
554+ ```
555+
556+ ### Performance Comparison: Async vs Sync
557+
558+ Here's a practical example comparing async and sync performance:
559+
560+ ``` python
561+ import asyncio
562+ import time
563+ from bloomy import Client, AsyncClient
564+
565+ def sync_bulk_create (todos_data ):
566+ """ Synchronous bulk creation."""
567+ start = time.time()
568+
569+ with Client(api_key = " your-api-key" ) as client:
570+ result = client.todo.create_many(todos_data)
571+
572+ duration = time.time() - start
573+ return result, duration
574+
575+ async def async_bulk_create (todos_data , max_concurrent = 10 ):
576+ """ Asynchronous bulk creation."""
577+ start = time.time()
578+
579+ async with AsyncClient(api_key = " your-api-key" ) as client:
580+ result = await client.todo.create_many(todos_data, max_concurrent = max_concurrent)
581+
582+ duration = time.time() - start
583+ return result, duration
584+
585+ # Compare performance
586+ async def performance_comparison ():
587+ # Create test data
588+ todos_data = [
589+ {" title" : f " Todo { i} " , " meeting_id" : 123 }
590+ for i in range (50 )
591+ ]
592+
593+ # Run sync version
594+ sync_result, sync_time = sync_bulk_create(todos_data)
595+
596+ # Run async version
597+ async_result, async_time = await async_bulk_create(todos_data)
598+
599+ print (f " Sync version: { sync_time:.2f } seconds " )
600+ print (f " Async version: { async_time:.2f } seconds " )
601+ print (f " Speed improvement: { (sync_time / async_time - 1 ) * 100 :.0f } % " )
602+
603+ # Run comparison
604+ asyncio.run(performance_comparison())
605+ ```
606+
607+ !!! note "Typical Performance Gains"
608+ - 10 items: 50-60% faster with async
609+ - 50 items: 70-80% faster with async
610+ - 100+ items: 80-85% faster with async (diminishing returns due to rate limiting)
611+
612+ ### Combining Multiple Async Bulk Operations
613+
614+ ``` python
615+ async def complete_meeting_setup ():
616+ """ Set up a complete meeting with all components."""
617+ async with AsyncClient(api_key = " your-api-key" ) as client:
618+ # Create meeting first
619+ meeting_result = await client.meeting.create_many([
620+ {" title" : " Q1 Planning Session" , " attendees" : [456 , 789 ]}
621+ ])
622+
623+ if not meeting_result.successful:
624+ return None
625+
626+ meeting_id = meeting_result.successful[0 ][' id' ]
627+
628+ # Create all meeting components concurrently
629+ todos_task = client.todo.create_many([
630+ {" title" : " Prepare Q1 roadmap" , " meeting_id" : meeting_id},
631+ {" title" : " Review budget allocation" , " meeting_id" : meeting_id}
632+ ])
633+
634+ issues_task = client.issue.create_many([
635+ {" title" : " Resource constraints" , " meeting_id" : meeting_id},
636+ {" title" : " Timeline concerns" , " meeting_id" : meeting_id}
637+ ])
638+
639+ goals_task = client.goal.create_many([
640+ {" title" : " Complete product launch" , " meeting_id" : meeting_id},
641+ {" title" : " Achieve 20% g rowth" , " meeting_id" : meeting_id}
642+ ])
643+
644+ # Wait for all operations to complete
645+ todos_result, issues_result, goals_result = await asyncio.gather(
646+ todos_task, issues_task, goals_task
647+ )
648+
649+ print (f " Meeting { meeting_id} setup complete: " )
650+ print (f " - Todos: { len (todos_result.successful)} " )
651+ print (f " - Issues: { len (issues_result.successful)} " )
652+ print (f " - Goals: { len (goals_result.successful)} " )
653+
654+ return meeting_id
655+
656+ # Run the setup
657+ asyncio.run(complete_meeting_setup())
658+ ```
659+
392660## Best Practices
393661
3946621 . ** Validate Input Data** : Check required fields before bulk operations
@@ -397,6 +665,8 @@ total_failed = sum(len(r.failed) for r in all_results)
3976654 . ** Use Appropriate Chunk Sizes** : Balance between efficiency and rate limits
3986665 . ** Implement Retry Logic** : For transient failures, consider retrying
3996676 . ** Monitor Rate Limits** : Watch for 429 errors and adjust accordingly
668+ 7 . ** Choose Async When Appropriate** : Use async for better performance with multiple items
669+ 8 . ** Control Concurrency** : Adjust ` max_concurrent ` based on your use case and API limits
400670
401671## Next Steps
402672
0 commit comments