Speckle objects support both static (typed) and dynamic (untyped) properties. Static properties are defined in the class with type hints and provide IDE autocomplete, while dynamic properties can be added at runtime for application-specific data.
Copy
from specklepy.objects.geometry import Point# Static properties (defined in class)point = Point(x=1.0, y=2.0, z=3.0)# Dynamic properties (added at runtime)point.custom_id = "POINT_001"point.metadata = {"author": "Alice"}
Learn more about Static vs Dynamic Properties including type checking, required vs optional properties, and how to work with both types.
Speckle organizes data in a three-level hierarchy:
Copy
Project └─ Model └─ Version └─ Object (Root object + children)
Projects are top-level containers for related work.
Contain multiple models
Have members with roles (owner, contributor, reviewer)
Unique ID and name
Copy
from specklepy.core.api.inputs.project_inputs import ProjectCreateInputproject = client.project.create(ProjectCreateInput( name="Office Building Renovation", description="Main project for the renovation"))project_id = project.id
Models are branches within a project, representing different variants or disciplines.
# Receive from serverreceived_object = operations.receive( obj_id=object_id, remote_transport=transport)# Data is automatically reconstructedprint(received_object.some_property)
Objects are serialized to JSON for storage and transport:
Copy
from specklepy.api import operations# Serialize to JSON stringjson_string = operations.serialize(my_object)# Deserialize from JSON stringreconstructed_object = operations.deserialize(json_string)
Most developers won’t need to know the serialization and deserialization functions directly. The send() and receive() operations handle serialization and deserialization implicitly. Use these functions only when you need custom workflows like saving objects to files or working with JSON representations directly.
Objects sent to the server but not referenced by any version are called “orphaned” or “zombie” objects. While they exist in the database and have object IDs, they are effectively unreachable and cannot be browsed or retrieved through the normal Speckle UI or workflows.
Copy
# This object is sent but becomes orphanedobject_id = operations.send(base=my_object, transports=[transport])# ⚠️ Without creating a version, this object is a "zombie"# Make it reachable by creating a versionversion = client.version.create(CreateVersionInput( project_id=project_id, model_id=model_id, object_id=object_id, # Now reachable via this version message="My data"))
Key Points:
Orphaned objects remain in the database but are not discoverable
You can still retrieve them if you have the object ID: operations.receive(obj_id=object_id)
Always create a version after sending important data
The server may eventually clean up orphaned objects (implementation-dependent)
from specklepy.core.api.inputs.version_inputs import CreateVersionInputversion_input = CreateVersionInput( project_id=project_id, model_id=model_id, object_id=object_id, message="Description of changes")version = client.version.create(version_input)
4
Get Version
Retrieve a version to access its data
Copy
# Get the version by IDversion = client.version.get(project_id=project_id, version_id=version.id)# Access the referenced object IDobject_id = version.referencedObject
5
Receive
Receive objects by ID
Copy
received = operations.receive(obj_id=object_id, remote_transport=transport)
Speckle uses a type system to preserve object types across different platforms:
Copy
from specklepy.objects.geometry import Pointpoint = Point(x=1, y=2, z=3)# Every object has a speckle_typeprint(point.speckle_type) # "Objects.Geometry.Point"# This allows proper deserializationreceived = operations.receive(...) # Comes back as Point, not Base
Connector CompatibilityMost Speckle connectors will not recognize custom types when receiving data into host applications. Your custom objects will likely be ignored during conversion to native application objects.Best Practice: Include a displayValue property with mesh geometry to ensure your custom objects are at least visible in the Speckle viewer and can be converted to generic native objects with geometry:
Copy
from specklepy.objects import Basefrom specklepy.objects.geometry import Meshclass CustomStructure(Base, speckle_type="MyCompany.Structure"): load_capacity: float material: str displayValue: list[Mesh] # Makes it visible and convertible# Create with display valuestructure = CustomStructure( load_capacity=5000.0, material="Steel")structure.displayValue = [mesh_representation] # Add after creation
See Display Values for more details on making your objects visible and interoperable.