Skip to main content

Overview

When receiving data from Speckle, you’ll encounter three distinct types of data structures. Understanding which type you’re working with helps you choose the right approach.
from specklepy.api import operations
from specklepy.transports.server import ServerTransport

# Receive data from Speckle
transport = ServerTransport(stream_id="project_id", client=client)
obj = operations.receive(object_id, remote_transport=transport)

# What type of data is this?
print(f"Type: {obj.speckle_type}")
print(f"Properties: {obj.get_member_names()}")

The Three Types

Type 1: Custom Data

What it is: Data you create from scratch with your own structure. Characteristics:
  • Simple Base objects with custom properties
  • No connector-specific structure
  • Direct property access
  • Full control over schema
Example:
from specklepy.objects import Base
from specklepy.objects.geometry import Point

# You create this structure
survey = Base()
survey.name = "Site Survey 2024"
survey.date = "2024-01-15"
survey.points = [
    Point(x=0, y=0, z=10.5),
    Point(x=100, y=50, z=12.3),
]
survey.notes = "Initial survey"
When you see it:
  • Python-to-Python workflows
  • Custom data pipelines
  • Analysis results you’re sending
  • Data you control end-to-end

Type 2: Simple Model Data

What it is: Geometry with attached properties, typically from simple exports or scripts. Characteristics:
  • Geometry objects (Point, Line, Mesh)
  • Properties as dictionaries or direct attributes
  • Optional displayValue for visualization
  • Material and layer information
  • Moderate nesting (1-2 levels)
Example:
{
    "speckle_type": "Objects.Geometry.Mesh",
    "vertices": [...],
    "faces": [...],
    "properties": {
        "Material": "Concrete",
        "Thickness": 200,
        "LoadBearing": true
    },
    "layer": "Walls",
    "color": "0xFF808080"
}
When you see it:
  • Grasshopper exports
  • Rhino script outputs
  • Simple geometry with metadata
  • Blender basic exports
  • CAD-like data

Type 3: Complex BIM Data

What it is: Rich application data from BIM tools with complex nested structures. Characteristics:
  • Deep nesting (3+ levels)
  • Rich property collections
  • Display value proxies
  • Encoded native data
  • Instance/definition patterns
  • Data chunk references
Example:
{
    "speckle_type": "Objects.Data.DataObject",
    "name": "Wall Instance",
    "properties": {
        "Material": "Concrete",
        "Thickness": 200,
        "Width": 0.2,
        "Function": "Exterior",
        "Volume": 15.5,
        "Area": 45.0,
        "LoadBearing": true
    },
    "displayValue": [
        {
            "speckle_type": "Objects.Geometry.Mesh",
            "vertices": [],
            "faces": []
        }
    ]
}
When you see it:
  • Connector exports from any BIM authoring tool
  • Rhino exports with complex objects
  • ArchiCAD, Revit, Tekla exports
  • Any application with rich metadata

Quick Decision Tree

from specklepy.api import operations
from specklepy.objects.data_objects import DataObject
from specklepy.objects.geometry import Mesh, Point, Line

obj = operations.receive(object_id, remote_transport=transport)

# What type of data is this? Check structural patterns, not speckle_type
if isinstance(obj, DataObject) or (hasattr(obj, "properties") and isinstance(obj.properties, dict)):
    print("Type 3: Complex BIM Data - check properties for semantics")
    if hasattr(obj, "properties"):
        category = obj.properties.get("category", "Unknown")
        print(f"Category: {category}")
    if hasattr(obj, "displayValue"):
        print("Has display value for visualization")

elif isinstance(obj, (Mesh, Point, Line)) or hasattr(obj, "vertices"):
    print("Type 2: Simple Model Data - geometry object")
    if hasattr(obj, "properties"):
        print(f"Properties: {obj.properties}")

else:
    print("Type 1: Custom Data - direct property access")
    print(f"Available properties: {obj.get_member_names()}")
Modern Approach: Instead of checking speckle_type strings, check for structural patterns: Does it have a properties dict? Does it have displayValue? Is it a geometry class? This works reliably across all Speckle versions.

Key Differences

  • Structure
  • Access Pattern
  • Traversal
Custom Data:
obj.myProperty  # Direct access
obj.myList[0]   # Simple nesting
Simple Model Data:
obj.properties["Material"]  # Properties dict
obj.displayValue.vertices   # Geometry access
Complex BIM Data:
obj.properties["Volume"]  # Properties dict access
obj.displayValue  # List of geometry representations

Practical Implications

Custom Data

Pros: Simple, predictable, fast
Cons: No standard structure, manual schema
Best for: Analysis results, custom pipelines, Python-to-Python

Simple Model Data

Pros: Geometry-focused, moderate complexity
Cons: Limited metadata, manual property inspection
Best for: Visualization, simple exports, geometry processing

Complex BIM Data

Pros: Rich information, application semantics
Cons: Complex structure, defensive code needed, larger data
Best for: BIM workflows, detailed analysis, cross-application exchange

Collections and Hierarchy

The elements Convention

Speckle uses a standardized convention for organizing hierarchical data: the elements property. This property has a special meaning and is the predictable way to traverse object hierarchies. Key Concepts:
  • Atomic Objects - Objects that represent actual “things” (walls, beams, points, etc.)
  • Property Objects - Objects stored as properties for reference (materials, styles, etc.)
  • Container Objects - Objects that organize other objects via elements
from specklepy.objects.models.collections import Collection
from specklepy.objects.geometry import Point, Line

# elements holds the hierarchy of atomic objects
layer = Collection(name="Site Points")
layer.elements = [
    Point(x=0, y=0, z=0),
    Point(x=10, y=0, z=0),
    Point(x=10, y=10, z=0)
]

# Nested collections create hierarchy
project = Collection(name="Project")
project.elements = [
    layer,  # Nested collection
    Line(start=Point(x=0, y=0, z=0), end=Point(x=100, y=0, z=0))
]
Why elements?While any property can technically contain Speckle objects, elements is the internal convention for managing hierarchy in a predictable way. This allows traversal code, connectors, and the viewer to consistently understand object relationships.Some typed properties also expect arrays of objects (like displayValue), but these serve different purposes - displayValue is for visualization, while elements is for logical hierarchy.

Collections

The Collection class formalizes this pattern:
from specklepy.objects.models.collections import Collection

# Collections represent organizational units
# Examples: Layers (Rhino/AutoCAD), Collections (Blender), Categories (Revit)
structural = Collection(
    name="Structural",
    applicationId="unique-id-123"  # Unique identifier
)

# Add elements to the collection
structural.elements = [
    column1,
    column2,
    beam1,
    beam2
]

# Nest collections for deeper hierarchy
building = Collection(name="Building")
building.elements = [
    structural,  # Nested collection
    architectural,  # Another nested collection
    mep  # Another nested collection
]
Collection Properties:
  • name - Human-readable name (not necessarily unique)
  • elements - List of contained objects (can include nested Collections)
  • applicationId - Unique identifier for the collection
  • speckle_type - "Speckle.Core.Models.Collections.Collection"
Collections map to familiar organizational structures:
  • Rhino/AutoCAD - Layers
  • Blender - Collections
  • Revit - Categories or Worksets
  • Custom - Any logical grouping you define

Atomic vs Property Objects

Understanding the difference helps you structure data correctly: Atomic Objects (stored in elements):
  • Represent actual entities in your model
  • Should be traversed when processing the model
  • Examples: walls, beams, points, meshes, equipment
Property Objects (stored as typed properties):
  • Referenced by atomic objects
  • Provide metadata or configuration
  • Examples: materials, render properties, styles, definitions
from specklepy.objects import Base
from specklepy.objects.other import RenderMaterial
from specklepy.objects.geometry import Mesh

# Material is a property object (not in elements)
steel_material = RenderMaterial(
    name="Steel",
    diffuse=0xFF808080,
    metalness=0.9
)

# Beam is an atomic object (goes in elements)
beam = Base()
beam.name = "Beam-001"
beam.geometry = Mesh(...)
beam.material = steel_material  # Reference as property

# Collection holds atomic objects
structure = Collection(name="Structure")
structure.elements = [beam]  # Atomic object in elements
# structure.elements does NOT contain steel_material

Traversing with elements

The elements convention enables predictable traversal:
def traverse_elements(obj, depth=0):
    """Traverse object hierarchy using elements convention"""
    indent = "  " * depth
    print(f"{indent}{obj.speckle_type}: {getattr(obj, 'name', 'unnamed')}")
    
    # Check for elements property
    if hasattr(obj, "elements"):
        for element in obj.elements:
            traverse_elements(element, depth + 1)

# Works consistently across all data types
project = operations.receive(object_id, remote_transport=transport)
traverse_elements(project)
Not all objects use elementsSimple geometry objects (Point, Line, Mesh) typically don’t have elements - they are leaf nodes. Always check hasattr(obj, "elements") before accessing it.

Mixing Types

Real-world data often mixes types, organized using the elements convention:
from specklepy.api import operations
from specklepy.objects.models.collections import Collection

obj = operations.receive(object_id, remote_transport=transport)

# Root might be a Collection
print(f"Project: {obj.name}")

# Iterate through elements (the standard way)
for element in obj.elements:
    print(f"Element: {element.speckle_type}")
    
    # Some might be simple geometry
    if element.speckle_type.startswith("Objects.Geometry"):
        print(f"  Simple geometry: {element}")
    
    # Some might be complex BIM objects
    elif hasattr(element, "properties"):
        print(f"  BIM object with properties")
    
    # Some might be nested Collections
    elif isinstance(element, Collection):
        print(f"  Nested collection: {element.name}")
        for child in element.elements:
            print(f"    Child: {child.speckle_type}")

Summary

Custom Data

You create it
  • Direct access
  • Simple structure
  • Full control

Simple Model

Geometry + Props
  • Properties dict
  • DisplayValue
  • Moderate nesting

Complex BIM

Rich BIM Data
  • Deep nesting
  • Properties dict
  • Defensive code
Choose your approach based on the data type:
  • Custom → Direct property access
  • Simple → Check common properties
  • Complex → Defensive traversal

Next Steps