diff --git a/include/mg_procedure.h b/include/mg_procedure.h
index 312693ba0..63ce6fc88 100644
--- a/include/mg_procedure.h
+++ b/include/mg_procedure.h
@@ -539,6 +539,16 @@ const struct mgp_edge *mgp_edges_iterator_get(
 /// NULL is returned if the end of the iteration has been reached.
 const struct mgp_edge *mgp_edges_iterator_next(struct mgp_edges_iterator *it);
 
+/// ID of an edge; valid during a single query execution.
+struct mgp_edge_id {
+  int64_t as_int;
+};
+
+/// Get the ID of given edge.
+/// The ID is only valid for a single query execution, you should never store it
+/// globally in a query module.
+struct mgp_edge_id mgp_edge_get_id(const struct mgp_edge *e);
+
 /// Copy a mgp_edge.
 /// Returned pointer must be freed with mgp_edge_destroy.
 /// NULL is returned if unable to allocate a mgp_edge.
diff --git a/include/mgp.py b/include/mgp.py
index 467164a3f..e411d6f5f 100644
--- a/include/mgp.py
+++ b/include/mgp.py
@@ -165,8 +165,11 @@ class EdgeType:
             return self.name == other
         return NotImplemented
 
-    def __hash__(self) -> int:
-        return hash(self.name)
+
+if sys.version_info >= (3, 5, 2):
+    EdgeId = typing.NewType('EdgeId', int)
+else:
+    EdgeId = int
 
 
 class Edge:
@@ -194,6 +197,13 @@ class Edge:
         '''Return True if `self` is in valid context and may be used.'''
         return self._edge.is_valid()
 
+    @property
+    def id(self) -> EdgeId:
+        '''Raise InvalidContextError.'''
+        if not self.is_valid():
+            raise InvalidContextError()
+        return self._edge.get_id()
+
     @property
     def type(self) -> EdgeType:
         '''Raise InvalidContextError.'''
@@ -229,7 +239,7 @@ class Edge:
         return self._edge == other._edge
 
     def __hash__(self) -> int:
-        return hash((self.from_vertex, self.to_vertex, self.type))
+        return hash(self.id)
 
 
 if sys.version_info >= (3, 5, 2):
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index 70224a228..7f036e229 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -1141,6 +1141,10 @@ const mgp_edge *mgp_edges_iterator_next(mgp_edges_iterator *it) {
   }
 }
 
+mgp_edge_id mgp_edge_get_id(const mgp_edge *e) {
+  return mgp_edge_id{.as_int = e->impl.Gid().AsInt()};
+}
+
 mgp_edge *mgp_edge_copy(const mgp_edge *v, mgp_memory *memory) {
   return new_mgp_object<mgp_edge>(memory, v->impl, v->from.graph);
 }
diff --git a/src/query/procedure/py_module.cpp b/src/query/procedure/py_module.cpp
index c139fbb35..0f843fca1 100644
--- a/src/query/procedure/py_module.cpp
+++ b/src/query/procedure/py_module.cpp
@@ -835,6 +835,14 @@ PyObject *PyEdgeIsValid(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   return PyBool_FromLong(self->py_graph && self->py_graph->graph);
 }
 
+PyObject *PyEdgeGetId(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
+  CHECK(self);
+  CHECK(self->edge);
+  CHECK(self->py_graph);
+  CHECK(self->py_graph->graph);
+  return PyLong_FromLongLong(mgp_edge_get_id(self->edge).as_int);
+}
+
 PyObject *PyEdgeIterProperties(PyEdge *self, PyObject *Py_UNUSED(ignored)) {
   CHECK(self);
   CHECK(self->edge);
@@ -884,6 +892,8 @@ static PyMethodDef PyEdgeMethods[] = {
      METH_NOARGS, "__reduce__ is not supported."},
     {"is_valid", reinterpret_cast<PyCFunction>(PyEdgeIsValid), METH_NOARGS,
      "Return True if Edge is in valid context and may be used."},
+    {"get_id", reinterpret_cast<PyCFunction>(PyEdgeGetId), METH_NOARGS,
+     "Return edge id."},
     {"get_type_name", reinterpret_cast<PyCFunction>(PyEdgeGetTypeName),
      METH_NOARGS, "Return the edge's type name."},
     {"from_vertex", reinterpret_cast<PyCFunction>(PyEdgeFromVertex),